为什么`Class&lt; T&gt; =='Boolean.class`在'T extends Comparable <?>时导致编译器错误?超T&GT;`? [英] Why does `Class&lt;T&gt; == Boolean.class` cause a compiler error when `T extends Comparable&lt;? super T&gt;`?

查看:364
本文介绍了为什么`Class&lt; T&gt; =='Boolean.class`在'T extends Comparable <?>时导致编译器错误?超T&GT;`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用泛型来抽象出可比数据类型,如下面提供的代码所示。这种情况来自Java Swing组件,特别是试图调整表模型以使用泛型。



我有一个解决这个问题的办法(下面的案例3A)。然而,在我到达该解决方案的过程中,当我将类签名从 T扩展为Comparable< T> 到<$ c $时,编译器的行为让我感到困惑c> T扩展Comparable <?为什么编译器希望Comparable是一个原始类型(下面的例子3A)?我期望类签名能够工作(例6A, Class< T扩展了Comparable< ;? super T>> ),但它需要强制转换并在使用时导致编译器错误 if(type == Boolean.class)



为什么2B,3A / B和4A / B允许 if(type == Boolean.class)但1A / B,2A,5A / B和6A / B会导致编译器错误?具体来说,我想在2A和2B的情况下解释这个错误,它们共享相同的类签名。



可运行的测试用例如下。提供外部 Score DetailedScore 类来演示一个涉及继承的案例,该案例强调了案例A和案例之间的区别B



ComparableTest.java

  public class ComparableTest< L,T extends Comparable< ;? super T>> // Case A 
// public class ComparableTest< L,T extends Comparable< T>> //案例B:在没有继承的情况下使用。
{
public static void main(String [] args)
{
new ComparableTest< String,Boolean>(new String(B),Boolean.TRUE);
new ComparableTest< String,Float>(new String(F),new Float(1f));
new ComparableTest< String,Score>(new String(S),new Score(5f));
new ComparableTest< String,Score>(new String(D),new DetailedScore< String>(DS.S,5f));
新的ComparableTest< String,DetailedScore<>>(新的String(DS),新的DetailedScore< String>(DS.DS,5f));
}

public ComparableTest(L标签,T值)
{
//案例1A:编译器错误:类型不匹配:无法从类 // Class< T> type = value.getClass(); // Q:为什么我不能使用Class< T>?

//案例2A:编译器警告:类型安全性:从Class // Class< T> type =(Class< T>)value.getClass(); //案例2B:如果'T extends Comparable< T>'(请参阅上面的类声明中的注释),这将起作用。

//案例3A:编译器警告:Comparable是一种原始类型。对泛型类型的引用可比< T>应该被参数化
Class< ;?扩展Comparable> type = value.getClass(); //问:为什么Comparable在这里是一个原始类型?

//案例4A:编译器警告:类型安全性:取消选中从类 // Class <?扩展Comparable<>>> type =(Class< ;? extends Comparable<>)value.getClass();

//案例5A:编译器警告:类型安全性:从Class // Class <?扩展了Comparable< T>> type =(Class< ;? extends Comparable< T>)value.getClass();

//案例6A:编译器警告:类型安全性:取消选中从类 // Class <?扩展Comparable< ;? super T>> type =(Class< ;? extends Comparable< ;? super T>>)value.getClass();

//案例1A,2A:编译器错误:不兼容的操作数类型Class< T>和Class< Boolean>
//案例2B,3A / B,4A / B:好的。
//案例5A / B,6A / B:编译器错误:不兼容的操作数类型类 if(type == Boolean.class)
{
System.out.println(Treating+ label +as boolean(+ type.getCanonicalName()+)) ;
} else if(type == Float.class){
System.out.println(Treating+ label +as float(+ type.getCanonicalName()+));
} else {
System.out.println(Treating+ label +as(+ type.getCanonicalName()+));
}

return;
}
}

分数。 java

  public class Score实现了Comparable< Score> 
{
私人浮动值;

公共分数(浮点值)
{
this.value = value;
return;
}

@Override
public int compareTo(Score o)
{
return this.value.compareTo(o.value); //为简洁


code $

$ b DetailedScore.java

  public class DetailedScore< D>延伸得分
{
私人D细节;
$ b $ publicScoreScore(D someDetail,Float value)
{
super(value);
this.detail = someDetail;
return;
}

public D getDetail()
{
return this.detail;


$ / code>


解决方案

Object的javadoc .getClass() ,返回类型 value.getClass() Class <? extends | T |> ,即 Class <?扩展Comparable>
$ b

表达式类型 value.getClass()在进一步使用之前会经历通配符捕获;因此我们看到捕获的类型 Class



围绕 type的问题Boolean.class ,根据JLS #15.21。 3 ,取决于一种类型是否可以铸造到另一种类型。如果在编译时证明不可能,则禁止在两种类型之间进行投射;在这种情况下, == test也被禁止。这是有道理的。



然而,JLS #5.5.1 可能有点草率。首先,让我们按照其确切的词。 Class > 只能在类型参数 a1时转换为 Class ,a2 不可证明是不同的。显然,我们不希望将 Class< Boolean> 转换为 Class< FLoat>



Class< T> Class< Boolean> JLS #4.5.1 < a>,由于 Boolean<:Comparable ,它们不可证明是不同的。因此应该允许。你的编译器禁止它,但javac8u45允许它。

遵循相同的推理,在所有情况下都应该允许 type == Boolean.class 。这是IntelliJ的行为。但javac8不;它禁止案件5和6。



重新检视JLS #4.5.1 Class <?扩展Number> Class< ;?由于边界 Number Runnable 没有子类型关系,所以扩展Runnable> 将是无法比拟的。这太限制了,因为可能有一个实现Runnable的Number的子类。事实上,javac8确实允许这两种类型进行转换或比较。

有趣的是,javac8会禁止比较 Class< ;? extends Boolean> Class< ;?扩展了Runnable> 。显然,布尔是最终的事实是一个因素。这听起来很像JLS # 5.5.1

看起来,对于明显不同的测试,javac8有时会使用转换转换测试。



事情是一团糟,幸运的是,我们总是可以添加一个中介投向一个共同的超类型来解决所有问题,所以这不是一个太大的问题

 狗狗=(狗)(动物)猫; 

Dog.class ==(Class)Cat.class


I'm using generics to abstract out Comparable data types, as illustrated in the code supplied below. This case arose from a Java Swing component, specifically from attempting to adapt table model to use generics.

I have a working solution to that problem (Case 3A below). However, during the process of arriving at that solution I became confused by the behavior of the compiler when I changed the class signature from T extends Comparable<T> to T extends Comparable<? super T>.

Why does the compiler want Comparable to be a raw type (Case 3A below)? I expected the class signature to work (Case 6A, Class<T extends Comparable<? super T>>), but it requires a cast and causes a compiler error when using if (type == Boolean.class).

Why do cases 2B, 3A/B, and 4A/B allow if (type == Boolean.class) but cases 1A/B, 2A, 5A/B and 6A/B cause a compiler error? Specifically, I'd like an explanation of this error in the case of 2A vs. 2B, which share the same class signature.

The runnable test cases are below. The external Score and DetailedScore classes are supplied to demonstrate a case involving inheritance, which highlights the difference between Case A and Case B.

ComparableTest.java

public class ComparableTest<L, T extends Comparable<? super T>> // Case A
//public class ComparableTest<L, T extends Comparable<T>> // Case B: Works when used without inheritance.
{
    public static void main(String[] args)
    {
        new ComparableTest<String, Boolean>(new String("B"), Boolean.TRUE);
        new ComparableTest<String, Float>(new String("F"), new Float(1f));
        new ComparableTest<String, Score>(new String("S"), new Score(5f));
        new ComparableTest<String, Score>(new String("D"), new DetailedScore<String>("DS.S", 5f));
        new ComparableTest<String, DetailedScore<?>>(new String("DS"), new DetailedScore<String>("DS.DS", 5f));
    }

    public ComparableTest(L label, T value)
    {
        // Case 1A: Compiler Error: Type mismatch: cannot convert from Class<capture#2-of ? extends Comparable> to Class<T>
//        Class<T> type = value.getClass(); // Q: Why can't I use Class<T>?

        // Case 2A: Compiler Warning: Type safety: Unchecked cast from Class<capture#2-of ? extends Comparable> to Class<T>
//        Class<T> type = (Class<T>) value.getClass(); // Case 2B: This works if 'T extends Comparable<T>' (see note in class declaration above).

        // Case 3A: Compiler Warning: Comparable is a raw type. References to generic type Comparable<T> should be parameterized
        Class<? extends Comparable> type = value.getClass(); // Q: Why must Comparable be a raw type here?

        // Case 4A: Compiler Warning: Type safety: Unchecked cast from Class<capture#2-of ? extends Comparable> to Class<? extends Comparable<?>>
//        Class<? extends Comparable<?>> type = (Class<? extends Comparable<?>>) value.getClass();

        // Case 5A: Compiler Warning: Type safety: Unchecked cast from Class<capture#2-of ? extends Comparable> to Class<? extends Comparable<T>>
//        Class<? extends Comparable<T>> type = (Class<? extends Comparable<T>>) value.getClass();

        // Case 6A: Compiler Warning: Type safety: Unchecked cast from Class<capture#2-of ? extends Comparable> to Class<? extends Comparable<? super T>>
//        Class<? extends Comparable<? super T>> type = (Class<? extends Comparable<? super T>>) value.getClass();

        // Case 1A, 2A: Compiler Error: Incompatible operand types Class<T> and Class<Boolean>
        // Case 2B, 3A/B, 4A/B: OK.
        // Case 5A/B, 6A/B: Compiler Error: Incompatible operand types Class<capture#4-of ? extends Comparable<T>> and Class<Boolean>
        if (type == Boolean.class)
        {
            System.out.println("Treating " + label + " as boolean (" + type.getCanonicalName() + ")");
        } else if (type == Float.class) {
            System.out.println("Treating " + label + " as float (" + type.getCanonicalName() + ")");
        } else {
            System.out.println("Treating " + label + " as (" + type.getCanonicalName() + ")");
        }

        return;
    }
}

Score.java

public class Score implements Comparable<Score>
{
    private Float value;

    public Score(Float value)
    {
        this.value = value;
        return;
    }

    @Override
    public int compareTo(Score o)
    {
        return this.value.compareTo(o.value); // for brevity
    }
}

DetailedScore.java

public class DetailedScore<D> extends Score
{
    private D detail;

    public DetailedScore(D someDetail, Float value)
    {
        super(value);
        this.detail = someDetail;
        return;
    }

    public D getDetail()
    {
        return this.detail;
    }
}

解决方案

See javadoc of Object.getClass(), the return type of value.getClass() is Class<? extends |T|>, i.e., Class<? extends Comparable>.

The type of expression value.getClass() undergoes wildcard capture before it's used further; therefore we see the captured type Class<capture#2-of ? extends Comparable> in messages.

The question surrounding type==Boolean.class, according to JLS#15.21.3, depends on whether one type can be casted to another. Casting between 2 types is forbidden if it's provable at compile time that it is impossible; in which case == test is forbidden too. That makes sense.

However, JLS#5.5.1 might be a little sloppy. First, lets follow its exact words. Class<a1> can be casted to Class<a2>, only if type arguments a1, a2 are not provably distinct. Obviously we don't want Class<Boolean> casted to Class<FLoat>.

For your case between Class<T> and Class<Boolean>, according to JLS#4.5.1, they are not provably distinct, due to Boolean<:Comparable. Therefore it should be allowed. Your compiler forbids it, but javac8u45 allows it.

Follow the same reasoning, type==Boolean.class should be allowed in all your cases. That is the behavior of IntelliJ. But javac8 does not; it forbids case 5 and 6.

Reexamining JLS#4.5.1, Class<? extends Number> and Class<? extends Runnable> would be incomparable, because bounds Number and Runnable have no subtype relationship. That would be too restrictive, because there could be a subclass of Number that implements Runnable. Indeed, javac8 does allow the two types to be casted or compared.

Interestingly, javac8 would forbid comparing Class<? extends Boolean> and Class<? extends Runnable>. Obviously, the fact that Boolean is final is a factor here. That sounds a lot like JLS#5.5.1.

It appears that for "provably distinct" testing, javac8 sometimes uses casting conversion test.

While this whole thing is a mess, fortunately we can always add an intermediary cast to a common supertype to resolve all the issues, so this is not too big of a problem

Dog dog = (Dog)(Animal)cat;

Dog.class == (Class)Cat.class

这篇关于为什么`Class&lt; T&gt; =='Boolean.class`在'T extends Comparable <?>时导致编译器错误?超T&GT;`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆