简体   繁体   English

如何避免Java Generics扩展Comparable接口中未经检查的强制转换?

[英]How to avoid unchecked casts in a Java Generics extended Comparable interface?

Why is the unsafe cast (T) needed in this generic interface? 为什么这个通用接口需要不安全的强制转换(T) If T is comparable to itself, ie implements ExtendedComparable<super of T> which means also ExtendedComparable<T> , then why does type erasure require ExtendedComparable<T> to be cast to T? 如果T与自身相当,即实现ExtendedComparable<super of T> ,这也意味着ExtendedComparable<T> ,那么为什么类型擦除需要将ExtendedComparable<T>强制转换为T?

/* @param <T> T must be comparable to itself or any of its superclass
 * (comparables are consumers, thus acc. to the PECS principle 
 * = producer-extends,consumer-super we use the bounded wildcard type "super")
 */   
public interface ExtendedComparable<T extends ExtendedComparable<? super T>> {
    Comparator<? super T> getComparator();
    default boolean greaterThen(T toCompare) {
        return getComparator().compare((T) this, toCompare) > 0;
    }
}

Because there is no guarantee that this is actually an instance of class T or even extend it. 因为无法保证this实际上是T类的实例,甚至无法扩展它。

For example consider this: 例如,考虑一下:

public class T0 implements ExtendComparable<T0> {...}
public class T1 implements ExtendComparable<T0> {...}

In T0 complies fine as it complies with the bound: T0 extends ExtendComparable<T0> and T0 is super of T0. T0符合条件,因为它符合边界: T0 extends ExtendComparable<T0> ,T0 T0 extends ExtendComparable<T0> T0。 In this case this is an instance of T0 here so you are fine; 在这种情况下, thisT0一个实例,所以你很好; the cast (T)this (thus (T0)this ) makes sense. 演员(T)this (因此(T0)this )是有道理的。

With T1 the declaration is correct also because the bound is applied to T0 no T1 , T is substituted T0 . 对于T1 ,声明也是正确的,因为绑定应用于T0T1T取代T0 However this is T1 and T1 is not super nor a child of T0 . 然而, thisT1T1不是超级也不是T0的孩子。 Yes, both implement ExtendedCompatible<T0> , but you cannot cast between siblings. 是的,都实现了ExtendedCompatible<T0> ,但是你不能在兄弟姐妹之间进行转换。 For example Integer and Double extend Number but (Integer) new Double(0.0) fails. 例如,Integer和Double扩展Number但是(Integer) new Double(0.0)失败。 So too does the cast (T) translated to (T0) fail. 所以也没有投(T)翻译成(T0)失败。

The assumption you are making is that T is going to be set to the same as the class that is been declared and currently there is no way to force those semantics. 您正在做的假设是将T设置为与已声明的类相同,并且当前无法强制使用这些语义。 I hope this will change at some point in future releases of the Java language but perhaps there is actual reason why the Java language "task force" are avoiding to do so. 我希望这将在Java语言的未来版本中的某些时候发生变化,但也许实际上存在Java语言“任务组”无法避免这样做的原因。

There is a way to avoid the cast altogether but is better when you make ExtendedCompatible an abstract class rather than an interface. 有一种方法可以完全避免强制转换,但是当你将ExtendedCompatible作为抽象类而不是接口时更好。

You can declare a final field of type T which value would be set by a protected constructor by extending class which in turn must pass this as its value: 您可以声明一个T类型的final字段,该值将由受保护的构造函数通过扩展类来设置,而class又必须将this作为其值传递:

public abstract class ExtendedCompatible<T extends ExtendedCompatible<? super T>> {
  private final T thiz;

  protected ExtendedCompatible(final T thiz) {
     if (this != thiz) throw new IllegalArgumentException("you must pass yourself");
     this.thiz = thiz;
  }
  ...

  public class MyExtendedCompatible extends ExtendedCompatible<MyExtendedCompatible> {
     public MyExtendedCompatible() {
           super(this);
     }
  }

The price you pay is the extra memory consumption of having a silly reference to itself and the added code/CPU burden of passing this to the parent constructor. 你付出的代价是很傻参考自身和传球的添加的代码/ CPU负担的额外内存消耗this对父类的构造。

Another would be to declare an abstract method to get the T (this): 另一种方法是声明一个抽象方法来获取T (this):

// Parent abstract class:
   protected abstract T getThiz();
// Child class... for each class:
   protected MyChildClass getThiz() { return this; }

Thanks. 谢谢。 Valentin is right. 瓦伦丁是对的。 Even if both types implement the same interface, this does not and should not make the cast between them to work. 即使两种类型都实现了相同的接口,这也不会也不应该使它们之间的转换工作。 And yes, there is no mechanism in Java to enforce passing in T the same class as the class being declared. 是的,Java中没有任何机制可以强制传入与正在声明的类相同的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM