有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java泛型,如何强制一个方法的两个参数来扩展一个超类,使其具有相同的类型?

假设我有一个类层次结构,如下所示:

class Vehicle;
class Car extends Vehicle;
class Plane extends Vehicle;

我有一个比较两个对象的函数

public <T extends Vehicle> generateDiff(T original, T copy)

在编译时,上面的方法保证两个对象是Vehicle,但不能确保两个对象的类型相同

generateDiff(new Car(), new Car()); //OK
generateDiff(new Plane(), new Plane()); //OK
generateDiff(new Car(), new Plane()); //WRONG

我可以在编译时使用泛型实现这一点吗

另外:目前,我已经实现了,如果两个对象的Class不相同,它将抛出异常。但我对此并不满意

提前谢谢


共 (6) 个答案

  1. # 1 楼答案

    是的,你可以(有点)

    类型T正在从参数中inferred删除,但您可以指定类型:

    MyClass.<Car>generateDiff(new Car(), new Plane()); // generates a compile error
    

    如果不键入该方法,则类型T被推断为满足所用边界的最窄类,因此对于参数CarPlane,将起作用的最窄类型是Vehicle,因此这两行是等效的:

    generateDiff(new Car(), new Plane()); // type is inferred as Vehicle
    MyClass.<Vehicle>generateDiff(new Car(), new Plane());
    

    上面的代码假定generateDiff()是一个静态方法。如果它是一个实例方法,那么可以键入类并在方法中使用该类型

  2. # 2 楼答案

    一旦你深入其中,它就会变得有点抽象。您还必须为函数提供类类型(见下文)。如果您想强制执行这种行为,我建议您编写单独的方法来接受您希望比较的类型。不管怎样:

    public <C extends Vehicle> void generateDiff(Class<C> type, C original, C copy);
    

    你可以这样使用它:

    generateDiff(Plane.class, new Plane(), new Plane()); // OK
    generateDiff(Car.class, new Car(), new Car()); // OK
    generateDiff(Plane.class, new Plane(), new Car()); // ERROR
    generateDiff(Vehicle.class, new Plane(), new Car()); // OK
    

    不知道为什么任何理智的人都想这么做!:)

  3. # 3 楼答案

    不,无论是否使用泛型,这都无法实现。本质上,您是在询问是否可以告诉编译器违反多态性规则

    即使您明确定义了一个没有泛型的方法,比如下面的方法,它仍然会接受任何一对扩展Vehicle的类

    void generateDiff(Vehicle maybePlane, Vehicle maybeCar) { ...
    

    有一种情况是例外,但我不建议这样做。如果对扩展Vehiclefinal类对象(或任何未扩展的类)调用该方法,则可以重写该方法以匹配该类对每个参数的签名。但是你需要明确地定义每一个

    class Vehicle;
    final class Car extends Vehicle;
    final class Plane extends Vehicle;
    
    void generateDiff(Car car1, Car car2) { ...
    void generateDiff(Plane plane1, Plane plane2) { ...
    
    generateDiff(new Car(), new Car()); // OK
    generateDiff(new Plane(), new Plane()); // OK
    generateDiff(new Car(), new Plane()); // No matching method
    
  4. # 4 楼答案

    严格地说,你的问题的答案是“不,不可能”

    然而,有一个workaround。创建一个方法<T extends Vehicle> VehicleDiffer<T> compare(T vehicleA),其中VehicleDiffer<T>有一个方法ReturnType with(T vehicleB)。现在,您可以执行以下调用:

    compare(new Car()).with(new Car()); // okay
    compare(new Plane()).with(new Plane()); // okay
    

    以下操作将失败:

    compare(new Car()).with(new Plane()); // with(Car) can't be called with argument type Plane
    
  5. # 5 楼答案

    在我看来,任何能够接受汽车作为论据的方法都不可能接受汽车和飞机。由于编译器不知道将出现什么类型的对象(可以是汽车、公共汽车、飞机),因此无法保证两个参数的类型完全相同。如果有人扩展汽车,创造一辆福特?这两个对象都是CAR类型

    确保这一点的唯一方法是在运行时使用自定义逻辑

  6. # 6 楼答案

    Can I achive this at compile time using Generics?

    没有,因为类型是在运行时设置的,但是如果向函数发送了无效类型,您可以简单地使用内省并引发异常,尽管如果是这样,那么它们可能在您的设计中存在缺陷,但这只是我的观点

    instanceof运算符将检查底层类型,因此您可以引发异常或执行更合适的操作

    public <T extends Vehicle> generateDiff(T original, T copy)
    

    因为你可能会比较不同的类型,也许你不应该有一个单一的函数,因为它需要相当数量的if else在每个类中相应地实现该函数可能更明智,这样它们就可以与适当类型的对象进行适当的比较,尽管我采取了相当数量的假设,这可能是错误的