1

考虑以下在 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();
}

可以在知道其返回类型的超类中实现 apublic static <T> T get()而无需将其作为参数传递或从Object?

更新:

在第一个版本中,我谈论的是抽象类。现在我删除了摘要,我正在使用普通课程。

4

2 回答 2

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();
于 2013-10-09T19:58:23.437 回答
0

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

但是为继承设计类是一件痛苦的事,并且充满了潜在的问题。这是一个常见的问题:如果您决定要使用 HashMap 来表示 Vehicles 的集合,该怎么办?问题在于,为子类编写 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:基于接口的类型系统对于解决创建基于类的类型系统(继承破坏封装)的问题很有用,并且可以成为某些问题的良好替代解决方案。

于 2013-10-09T20:25:20.970 回答