Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ionic-framework/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 泛型与继承角情况_Java_Generics - Fatal编程技术网

Java 泛型与继承角情况

Java 泛型与继承角情况,java,generics,Java,Generics,我有以下代码,它的行为与我所想的不一样。我已经把这些评论放在了一起,说明了正在发生的事情和人们的期望 class C<T> { void m(T arg) { } } interface I { void m(Class arg); } class D extends C<Class<String>> implements I { } // expected : error -- conflicting inherited meth

我有以下代码,它的行为与我所想的不一样。我已经把这些评论放在了一起,说明了正在发生的事情和人们的期望

class C<T> {
    void m(T arg) {
    }
}

interface I {
    void m(Class arg);
}

class D extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: error -- abstract method not overridden

abstract class E extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: no error, but no bridge method
C类{
无效m(T参数){
}
}
接口I{
无效m(arg类);
}
类D扩展了C实现I{
}
//应为:错误--继承的方法冲突
//实际值:错误--未重写抽象方法
抽象类E扩展了C实现I{
}
//应为:错误--继承的方法冲突
//实际:无错误,但无桥接方法

有人能帮我理解这个行为吗。

D
没有继承
I
的任何内容,因为它没有默认方法,只有一个方法签名,而您确实没有为其提供实现。正如@RC指出的,您的
m
的类型差异很大,因此不认为它是有效的实现。要获得关于冲突的错误,实际上需要添加冲突的实现


E
中,您将类标记为抽象类,因此可以不按照接口中的要求实现方法。只要在扩展了
E
的任何类中执行此操作,就会触发关于冲突实现的错误,因为在类型擦除之后,类型将是相同的。基本上,您只是将问题从
D
转移到将要扩展的
E
我希望在
jdk8
中进行以下讨论

在您的代码中:

class D extends C<Class<String>> implements I {
让我们看看为什么应该在接口
I
中实现该方法。根据
JLS8.4.8.1

An instance method mC declared in or inherited by class C, overrides from C another
method mI declared in an interface I, iff all of the following are true:
• I is a superinterface of C.
• mI is an abstract or default method.
• The signature of mC is a subsignature (§8.4.2) of the signature of mI.
在上述代码中,
mC
void m(Class arg)
在Class
D
中,
mI
void m(Class arg)

并参见子签名的规范:

And the `JLS8.4.2` explain the subsignature between two method:

The signature of a method m1 is a subsignature of the signature of a method m2 if
either:
• m2 has the same signature as m1, or
• the signature of m1 is the same as the erasure (§4.6) of the signature of m2.
根据上述规范,
void m(Class arg
不是
void m(Class arg)
的子签名,但是
void m(Class arg)
void m(Class arg
的子签名。因此类
D
应该显式地实现接口
I

请参阅以下代码来解释它:

class C {
    public void m(Class arg) {
    }
}

interface I<T> {
    void m(T arg);
}

class D extends C implements I<Class<String>> {

}

根据上述规范,带有
T
Class
参数的
void m(T arg)
方法将转换为
void m(Class arg)
。转换后的方法将被擦除为
void m(Class arg)
,此方法将存在于类
D
中,因此类
D
不会继承类
C
中的方法
void m(t arg)

我尝试一步一步地解释发生了什么:

D
扩展了
C
,它引入了带有签名
void m(类arg)的方法
在类
C
定义后的编译时。但是由于类型擦除,编译后类型参数
字符串的信息将被删除。因此字节码将创建一个由类
C
定义的方法
void m(class arg)

此外,类
D
实现接口
I
,该接口要求
D
实现方法
m(类arg)
,该方法在编译时不同于方法
m(类arg)
由于类型参数,因此必须为前一个方法提供实现或声明
D
abstract

如果您为接口
I
声明的方法
m(Class arg)
提供实现,那么实际上您将重写Class
C
定义的方法,因为在应用类型擦除后,两者将具有相同的签名

如果你让class
D
extend
C
,事情就会改变。在这种情况下,class
C
将引入一个在编译时带有签名
void m(Collection arg)
的方法,该方法在发生类型擦除后变为
void m(Collection args)
,作为字节码。但是现在该方法
void m(class arg)
由接口
I
声明的方法具有不同的签名,因此不会重写而是重载类
C
定义的方法
m

下面是一个简单的Java示例:

class C<T> {
    void m(T arg) {
        System.out.println("Method [m] of class [C] called");
    }
}

class D extends C<Class<String>> implements I {
    @Override
    public void m(Class arg) {
        System.out.println("Method [m] of class [D] called");
    }
}

class DD extends C<Collection<String>> implements I {

    @Override
    public void m(Class arg) {
        System.out.println("Method [m] of class [DD] called");
    }
}

问题可以归结为Java泛型的一个基本事实,即将实例化泛型类型分配给原始类型是安全的,但反之则不正确。重写方法时,不允许不安全的转换。因此,
Class
无法重写原始类类型

在这种情况下:

    Class a = String.class; //type safe
    Class<String> b = a; //not type safe
Class a=String.Class;//类型安全
类b=a;//不是类型安全的

井类与类的类型不同解决方案可能是在I
I
中添加一个类型,并在I中添加
m(类
class C<T> {
    void m(T arg) {
        System.out.println("Method [m] of class [C] called");
    }
}

class D extends C<Class<String>> implements I {
    @Override
    public void m(Class arg) {
        System.out.println("Method [m] of class [D] called");
    }
}

class DD extends C<Collection<String>> implements I {

    @Override
    public void m(Class arg) {
        System.out.println("Method [m] of class [DD] called");
    }
}
public static void main(String[] args) {
    new D().m(D.class);
    new D().m((Class<String>) null);

    new DD().m(DD.class);
    new DD().m(new ArrayList<String>());
}
Method [m] of class [D] called
Method [m] of class [D] called
Method [m] of class [DD] called
Method [m] of class [C] called.
    Class a = String.class; //type safe
    Class<String> b = a; //not type safe