public class gen1<T>{
    T var1;

    public gen1(T arg1)
    {
        var1=arg1;
    }
    void meth1(T arg1){

    }

    void meth2(gen1<? extends RuntimeException> arg)
    {

    }
}

public class gen2 extends gen1{
    public gen2(){
        super(new Exception());
        meth2(new gen1(Integer.valueOf(12))); //This should throw an error but it doesn't 
    }
}

以上是gen1泛型类. 下面是gen2类,它扩展了gen1

为什么第"meth2(new gen1(Integer.valueOf(12)));"行没有抛出错误? 我违反了上限,即类型参数应该扩展RuntimeException,但在这里我传递了一个对象

推荐答案

上面是一个Gen1泛型类.下面是Gen2类,它扩展了Gen1

你已经把问题埋在这里了.在Java中,您不仅仅继承父类的类型变量.如果您想让gen2也继承‘这个类是参数化的;它有一个类型变量’这一概念,您需要这样声明它:

public class gen2<T> extends gen1<T> {}

您在代码片段中执行的操作是extends gen1,这意味着您已经删除了泛型:您现在处于遗留/原始模式,all generics and all benefits of it, for all uses of that entire class everywhere已经消失.不应该使用遗留/原始版本,但您在这里使用它.在这种情况下,类型推理如何工作?答案很简单,不是这样的.

即使在使用原始/遗留版本的情况下,似乎不会对编译器的类型推断能力产生负面影响,也会丢失类型推断.这不是它的工作方式:您处于原始模式,因此关闭了all个基于泛型的推理,无论是否需要这样做才能使原始/遗留工作.

一百零二

因为您忽略了它,所以Gen2的所有S签名中的所有T,包括从Gen1继承的签名,都变成了它的上限,即Object,所以,meth2(someInstanceOfGen1)就可以了,因为meth2方法需要作为参数1 Object,这就是您要传递的.

new gen1(Integer.valueOf(12))也可以很好地工作--new gen1表示您处于原始模式(您想要new gen1<RuntimeException>(Integer.valueOf(12))--这会生成编译时错误)--如果您想要类型推断,您想要new gen1<>(Integer.valueOf(12)),这将推断您打算编写new gen1<Integer>--这是否可以取决于您对它做了什么.在这里,您将其传递给meth2,这很好,因为由于class gen1 extends gen2,meth2是原始/遗留模式.

那么,你应该怎么做呢?

public class Gen1<T> {
    T var1;

    public gen1(T arg1)
    {
        var1=arg1;
    }
    void meth1(T arg1){

    }

    void meth2(gen1<? extends RuntimeException> arg)
    {

    }
}

public class gen2<T> extends gen1<T> {
    public gen2(){
        super(new Exception());
        meth2(new gen1<>(Integer.valueOf(12))); // compile time error here
    }
}

你说‘这应该抛出一个错误’,这也不是它的工作方式,你对这个东西理解得相当彻底.

Generics are ENTIRELY a figment of the compiler's imagination.

在运行时,泛型do not exist in any way.java.exe的人不知道他们是什么.在从.java文件到.class文件的转换中,大多数泛型都完全消失了(javac使用它们来生成警告、错误和注入静默强制转换,然后大部分消除它们),类文件中保留的少数泛型被运行库视为注释-它只是跳过它们,不知道它们的含义,也不会以任何方式影响运行库所做的事情.它们只存在于类文件中,以便javac知道当您在编译某些代码时将类文件放在类路径上时要做什么,仅此而已.

而"抛出"完全是运行时的事情.编译器不会"抛出"任何东西.它会产生编译器错误或警告.因此,为什么不能将违反泛型描述为导致"扔"东西.它们生活在不同的领域;一个只关注编译器,一个只关注运行时.

在最好的情况下,陷入泛型警告(编译器明确告诉您:由于使用原始类型和/或强制转换为泛型而无法正确完成,因为强制转换是运行时的事情,而泛型是编译时的事情,我实际上不能保证类型安全),将导致在运行时在包含零强制转换的行上抛出ClassCastException个错误(因为CCEx是由编译器悄悄注入的强制转换导致的).

Java相关问答推荐

gitlab ci不会运行我的脚本,因为它需要数据库连接'

如何在Java中声明未使用的变量?

在Java Stream上调用collect方法出现意外结果

使用@MappdSuperClass扩展ParentClass&Won t继承ParentClass属性

使用联接和分页的SpringBoot Spring数据JPA

是否在允许数组元素为空时阻止 idea 为空性警告?

相同的Java SerializedLambda为implMethodKind返回不同的结果

内存和硬盘中的Zip不同,这会导致下载后的Zip损坏

使用htmlunit和java单击按钮

有没有可能在时间范围内得到多种解决方案?

try 使用类来包含JSON响应

为什么StandardOpenOption.CREATE不能通过Ubuntu在中小企业上运行?

找出承载Cargo 的最小成本

每次我需要时创建和关闭数据库连接会有什么效果吗?

泛型与泛型问题的完美解决方案?

如何对存储为字符串的大数字数组进行排序?

如何使用带有可选参数的类生成器?

如何在MPAndroidChart中的条形图上正确添加标签

ReturnedRect在升级后反转

JavaFX中ListView中的问题