1

给定一个接口,例如,

public interface NumVal<C extends Number>{ 
      /* Put your most sophisticated code ever here! */
} 

使用 var 之类的运行时是否有任何实际差异

NumVal myRawVal = (...)对比

NumVal<Number> mySuperVal = (...)?

我知道在编译时前者会导致更多警告。但是根据Oracle,类型擦除之后也应该是Number,所以无论如何它是安全的,不是吗?

4

3 回答 3

2

您似乎对擦除NumVal' 声明的类型变量C.

public interface NumVal<C extends Number>{ 

上面的声明引入了一个新的泛型类型,名为NumVal. 是这种类型的参数NumVal<Integer>化的一个例子。是原始类型NumVal

根据JLS 定义C,其中的擦除,

类型变量的擦除(第 4.4 节)是其最左边界的擦除。

Number,在这些概念定义中永远不会发挥作用。

作为原始类型的事实会NumVal影响您如何使用该类型的任何引用表达式。当表达式中涉及原始类型时,作为方法参数,作为方法调用目标等,表达式的其他部分也将被删除。由于它们被删除,编译器无法保证表达式的类型安全,因此会警告您。

规范中详细讨论了潜在问题:

于 2015-12-15T18:16:05.027 回答
1

如果您错误地将 Dog 放入您的 Cats 集合中,并且如果您在任何地方都正确指定了类型参数,那么编译器将引发错误,从而帮助您纠正错误,并避免在运行时真正不必要的意外意外。

如果您不指定类型参数,编译器将无法帮助您,并且您可能会在房间时间遇到一些非常糟糕且非常不必要的惊喜。如果你的代码完美,那么你会没事的,你不需要帮助。但是没有人能完美地编码,没有人。

我的意思是狗是否是猫,如果它们推动动物,如果接口 Zoo 被定义为 Zoo,它是否 Zoo(原始)与 Zoo 相同

在运行时,类型被擦除。Zoo<Animal>, Zoo<T extends Animal>,Zoo<Cat>等都只是Zoo. 所以是的,您可以<...>从代码中删除所有这些类型参数。如果您的代码以前可以完美运行,那么它将继续完美运行。对于运行时的程序,你是否编写了正确的类型参数并不重要。但是如果你没有指定类型参数,那么你可能会犯错误。

类型参数是为了保护您,在编译时尽早发现问题,而不是稍后。所以总是使用最合适的类型,永远不要使用原始类型。

于 2015-12-15T17:26:48.287 回答
0

尽管有很好的答案,但感谢@JB Nizet,我看到了声明是错误的案例,并且由于他没有冒险将其改编为答案,我将在这里用这个测试用例进行总结:

public class NumVal<C extends Number> {
    Integer       nextFibboInt  (int secondLast,int last){ return sum(secondLast,last);};
    C             nextFibboC    (C secondLast,    C last){ return sum(secondLast,last);};
    List<Integer> next2FibbosInt(int secondLast,int last){ return Arrays.asList(sum(secondLast,last), sum(sum(secondLast,last),last));};
    List<C>       next2FibbosC  (C secondLast,    C last){ return Arrays.asList(sum(secondLast,last), sum(sum(secondLast,last),last));};

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void main (String[] args){

        Integer  singleGenericSubtype    = new NumVal<Integer>().nextFibboC(1,2);          //This is the exclusive obvious for generics
        Integer  listGenericSubtype      = new NumVal<Integer>().next2FibbosC(1,2).get(0); //This is the exclusive obvious for generics

        Number   singleGenericSuper      = new NumVal<Number> ().nextFibboC(1,2);
        Integer  singleHardcodedTyped    = new NumVal<Integer>().nextFibboInt(1,2);
        Number   listGenericSuper        = new NumVal<Number> ().next2FibbosC(1,2).get(0);
        Integer  listTyped               = new NumVal<Integer>().next2FibbosInt(1,2).get(0);

        Number   singleGenericSuperRaw   = new NumVal().nextFibboC(1,2);            // OK - These are the cases I was assuming as equivalent
        Integer  singleHardcodedTypedRaw = new NumVal().nextFibboInt(1,2);          // OK - These are the cases I was assuming as equivalent
        Number   listGenericSuperRaw     = new NumVal().next2FibbosC(1,2).get(0);   // KO - These are the cases that I did not considerate that fail the hypothesis
        Integer  listHardcodedTypedRaw   = new NumVal().next2FibbosInt(1,2).get(0); // KO - These are the cases that I did not considerate that fail the hypothesis
    }

    private <T extends Number> T sum(T o1, T o2) {
        Number sum = new BigDecimal(o1.toString()).add(new BigDecimal(o2.toString()));
        Object result = -1;
        try { result = o1.getClass().getMethod("valueOf",String.class).invoke(null, sum.toString()); } catch (Exception e){}
        return (T) result;
    }
}

  • 所以最后 2 个案例失败(在编译时),这意味着擦除的 raw 不能与 super 互换<Number>。使类原始似乎会删除该类的任何类型的任何方法签名(感谢@JB Nizet)
    • 第一个失败是因为即使 C 在单独时被正确擦除为 Number,List<C>否则也会被擦除为List<Object>
    • 第二次失败甚至更奇怪,因为令人惊讶的是我的原始类正在擦除List<Integer>硬编码返回方法的类型,即使它们没有以任何方式使用/与类 C 类型相关
于 2015-12-16T03:45:54.457 回答