如何绑定子类的类类型

时间:2016-02-18 09:49:13

标签: java generics

我有这堂课:

public abstract class Addressable {

  abstract <T extends "this" Addressable> void hardEquals(T t);

}

方法hardEquals(T t)不是我想要的。我想要的是将T绑定为this的同一类。换句话说,当我使用具体类Addressable扩展类MyAddressable时,我希望方法hardEquals()具有签名:

void hardEquals(MyAddressable t);

为了完整性:

public class MyAddressable extends Addressable {

  void hardEquals(MyAddressable t);

}
  1. 有可能实现我的目标吗?
  2. 如果答案是否定的,我的班级约束是否如此愚蠢?

4 个答案:

答案 0 :(得分:2)

一般来说,这是不可能的。在考虑一下你的场景时,问题就变得清晰了。您拥有该类Addressable及其方法hardEquals,它要求其参数的类型等于此。然后你有子类

public class MyAddressable extends Addressable {
  void hardEquals(MyAddressable t);
}

似乎按预期工作。现在想象一下子类的子类:

public class GrandChild extends MyAddressable {
  void hardEquals(MyAddressable t);
}

在这里,您有一个必须接受MyAddressable作为hardEquals'参数的类,因为不允许通过缩小参数类型来覆盖方法,即不接受超类接受的东西。毕竟,您总是可以使用MyAddressable类型的变量来实际引用GrandChild的实例。将参数限制为hardEquals以匹配对象的实际运行时类型是不可能的。

所以期望的“参数类型必须匹配this类型”规则与“子类方法必须接受超类所做的所有参数”规则冲突。

请注意,这通常与类型系统的实际限制相混淆,同样的事情对返回类型不起作用。由于允许为子类缩小返回类型,因此在某些情况下保证返回this类型的愿望可能是合理的,例如:

class Base {
    Base/*actually this type*/ clone() { … }
}
class SubClass extends Base {
    SubClass/*actually this type*/ clone() { … }
}
class GrandChild extends SubClass {
    GrandChild/*actually this type*/ clone() { … }
}

有效,但没有正式的方法来指定保证this类型被返回,因此由程序员的规则来为每个子类添加正确的覆盖。

但是,如上所述,对于参数类型,这通常不起作用,因为您无法缩小子类中参数的类型。

答案 1 :(得分:1)

你可以从Comparable借用一个技巧,并在类声明本身中指定泛型类型:

public abstract class Addressable<T> {
    abstract <T> void hardEquals(T t);
}

这会产生稍微笨重的类定义,但它应该满足您的要求

public class HomeAddress extends Addressable<HomeAddress> {
    void hardEquals(HomeAddress t) {
        // implementation
    }
}

答案 2 :(得分:1)

我不知道问题的清洁解决方案。

你可以考虑这样的事情:

public abstract class Addressable<T extends Addressable<T>> {

  abstract void hardEquals(T other);

}

public class MyAddressable extends Addressable<MyAddressable> {

  @Override
  void hardEquals(MyAddressable other) {
    // ...
  }

}

但在某些情况下这可能会失败,例如:

public class FakeAddressable extends Addressable<MyAddressable> {

  @Override
  void hardEquals(MyAddressable aT) {
    // ...
  }

}

答案 3 :(得分:1)

如果您正在寻找能够才能与完全相同类型的对象一起使用的hardEquals,那么此类内容应该有效:

public abstract class Addressable<T extends Addressable<T>> {

    abstract boolean hardEquals(T t);

}

class X extends Addressable<X> {

    @Override
    boolean hardEquals(X t) {
        return true;
    }

}

class Y extends Addressable<Y> {

    @Override
    boolean hardEquals(Y t) {
        return true;
    }

}

public void test() {
    X x = new X();
    if ( x.hardEquals(x)) {
        System.out.println("Ok");
    }
    Y y = new Y();
    // Fails!
    if ( x.hardEquals(y)) {
        System.out.println("Not OK");
    }
}

正如@Holger正确地指出的那样 - 在扩展子类时这没有用。