超类返回子类类型

时间:2013-10-09 19:45:14

标签: java generics inheritance polymorphism

考虑以下场景在OO文献中非常常见:

public class Vehicle {
}

public class Car extends Vehicle {
}

public class Bike extends Vehicle {
}

现在,假设我想创建一个总是返回子类类型的函数get(),这样我就可以得到一个子类实例:

public static void main(String[] args) {

    Car car = Car.get();
    Bike bike = Bike.get();
    car.Start();
    bike.Start();
}

可以在超类中实现一个知道其返回类型的public static <T> T get(),而不将其作为参数传递或从Object转换?

更新

在第一个版本中,我谈论的是抽象类。现在我删除了抽象,我使用常规类。

2 个答案:

答案 0 :(得分:1)

在这种情况下,如果在超类上调用静态方法,Java不知道要返回哪种类型的子类。你提到你想要在不传递参数的情况下这样做,你不能。如果你可以传入一个参数,你可以这样做:

package com.stackexchange.stackoverflow;

public class Question19281170 {
  public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    Car car = Vehicle.get(Car.class);
    Bike bike = Bike.get(Bike.class);
    car.start();
    bike.start();
  }

  static abstract class Vehicle {
    public void start() {
      System.out.println(String.format("Start %s", toString()));
    }
    public static <T extends Vehicle> T get(Class<T> clazz) throws IllegalAccessException, InstantiationException {
      return clazz.newInstance();
    }
    @Override
    public String toString() {
      return "Vehicle";
    }
  }

  static class Car extends Vehicle {
    @Override
    public String toString() {
      return "Car";
    }
  }

  static class Bike extends Vehicle {
    @Override
    public String toString() {
      return "Bike";
    }
  }
}

最佳做法是使用多态:

Vehicle car = new Car();
Vehicle bike = new Bike();

答案 1 :(得分:0)

继承是一个强大的功能,非常适合构建基于类的层次结构,例如JavaFX中的层次结构。

但是设计继承类是一种痛苦,充满了潜在的问题。这是一个常见问题:如果您决定使用HashMap来表示车辆集合,该怎么办?问题是以不破坏equals()和hashcode()的契约的方式为子类编写equals()和hashcode()方法比听起来更难。此外,继承会破坏封装,并使您的子类依赖于超类中的实现细节,这可能会使您的代码变得脆弱或更难以维护。

继承的替代方法是使用基于接口的类型系统,类似于Java Collections API使用的类型。

public interface Vehicle {
    public void start();
    :
}

public class Car implements Vehicle {
}

public class Bike implements Vehicle {
}

接口用作基本类型,具有几个优点。最重要的是,无论类是什么,任何实现Vehicle接口的类都可以传递给任何要求Vehicle的方法。虽然这看起来类似于继承,但是没有实际的基于类的层次结构。实现细节,封装和equals()合同的所有问题都消失了。

基于接口的类型系统的缺点有几个:

  • 您必须能够使用接口定义以健壮的方式指定基本类型的行为。如果没有这个,作为基类型的界面就变得不方便了。

  • 接口必须由每个使用它的类完全实现(除非您为接口创建抽象骨架实现类,例如Collections API中的AbstractList)。

  • 很难改变界面......但编辑超类通常会有问题。请记住,对超类的更改可能会在其子类中产生副作用,这些副作用依赖于超类中的实现细节。

使用基于接口的类型会使您的情况看起来像这样(使用静态工厂):

Vehicle car = Car.newInstance();
Vehicle bike = Bike.newInstance();

car.start();
bike.start();

TL; DR:基于接口的类型系统可用于解决创建基于类的类型系统的问题(继承中断封装),并且可以成为某些问题的良好替代解决方案。