给出以下课程:
public class FooTest {
public static class Base {
}
public static class Derived extends Base {
}
public interface Service<T extends Base> {
void service(T value);
}
public abstract class AbstractService<T extends Derived> implements Service<T> {
public void service(T value) {
}
}
private AbstractService service;
public void bar(Base base) {
if(base instanceof Derived) {
service.service(base); // compile error at this line
}
}
}
使用以下pom.xml
构建课程时:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mgm-tp</groupId>
<artifactId>java-compiler-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerId>eclipse</compilerId>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-compiler-eclipse</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
在maven 3.4中它产生以下编译错误:
[错误]无法执行目标org.apache.maven.plugins:maven-compiler-plugin:3.3:在项目java-compiler-test上编译(default-compile):编译失败 [错误] C:\ Users \ abrieg \ workingcopy \ java-compiler-test \ src \ main \ java \ FooTest.java:[25] FooTest.Service类型中的方法服务(FooTest.Base)不适用于参数(FooTest.Base)
当eclipse编译器的源和目标级别设置为1.7或者使用javac
作为编译器时,没有报告编译错误。
问题是JLS 1.8对类型推断更具体,因此这个代码实际上不被eclipse编译器为java 1.8所假设,或者这是eclipse编译器中的回归。
根据编译器错误的文本,我倾向于说它是回归,但我不确定。
我已经确定已向jdt报告了以下两个错误,但我认为它们并不完全适用:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=432603
https://bugs.eclipse.org/bugs/show_bug.cgi?id=430987
如果这是回归,是否已将此报告给jdt?
答案 0 :(得分:2)
根据我的理解,这段代码应该编译,但当然不是没有未经检查的警告。
您已声明原始类型 service
的变量AbstractService
,它是原始类型 Service
的子类型有一个方法void service(Base)
,它是void service(T)
的删除。
因此,调用service.service(base)
可以调用void service(Base)
中声明的方法Service
,当然,使用未经检查的警告,因为该方法是通用的,无需验证类型参数T
发生了。
这可能违反直觉,因为AbstractService
类型使用擦除为void service(Derived)
的方法覆盖该方法,但此方法只能覆盖泛型中的其他方法上下文,而不是原始类型继承关系。
或者,换句话说,类型不能覆盖方法,因为它对参数类型的限制比被覆盖的超类型方法更严格。
这也适用于泛型类型继承,但适用于不同的结果。如果您的变量的类型为AbstractService<X>
,则由于类型参数的约束,X
必须可分配给Derived
。此类型AbstractService<X>
是Service<X>
的子类型,其方法为void service(X)
(T
:= X
),由{{1}覆盖(实现)使用方法AbstractService<X>
接受相同的参数类型。
由于您的网站似乎存在一些混淆,我想强调这与您的void service(X)
声明无关。如上所述,此行为是由于原始类型用法,这意味着您使用if(… instanceof Derived)
而没有实际类型参数,并且基本上关闭了泛型类型检查。如果你写了
AbstractService
如果您将变量的声明更改为
public void bar(Base base) {
service.service(base); // UNCHECKED invocation
}
它将不再是原始类型,并且将进行类型检查,private AbstractService<Derived> service;
将生成编译器错误,无论您是否将其与service.service(base)
括起来或不
原始类型仅与pre-Generics代码兼容,您应该避免使用它们,而不要忽略原始类型使用引发的警告。
答案 1 :(得分:0)
这当然是关于类型安全的
正如其他人所说,你需要将基础转换为Derived以使其满足服务要求(T值),因为编译器知道AbstractService.service的参数必须扩展Derived,所以如果它不是Derived,它就不适合
不幸的是,它摆脱了编译器错误,但它无法解决问题。这就是你得到类型安全警告的原因。
由于AbstractService
的定义,service
实际上是AbstractService<? extends Derived>
,如果你修复了这个遗漏,你就会收到错误。
这是因为AbstractService<? extends Derived>
未扩展AbstractService<Derived>
并且基础衍生到Derived修复了AbstractService<Derived>
的问题。
查看下面的课程以查看此示例。 ConcreteService
不延伸AbstractService<Derived>
它延伸AbstractService<RealDerived>
。您不能将任何Derived
传递给ConcreteService.service(RealDerived base)
,例如,您不能传递FalseDerived
对象,因为它不满足参数类型。
事实是,在某些地方必须实际定义T是什么,以便我们可以解决类型安全问题。
通常需要一些技巧,比如将实际类型的参数与服务的实际类型联系在一起的类,以便可以以类型安全的方式完成转换。
如下
public class FooTest {
public static class Base {
}
public static class Derived extends Base {
}
public interface Service<T extends Base> {
void service(T value);
}
public abstract static class AbstractService<T extends Derived> implements Service<T> {
public void service(T value) {
}
}
// The following class defines an argument B that and ties together the type of the service with the type of the referenced stored class which enables casting using the class object
public abstract static class Barrer<B extends Derived>{
private AbstractService<B> service;
private Class<B> handledClass;
protected Barrer(Class<B> handledClass, AbstractService<B> service){
this.handledClass = handledClass;
this.service = service;
}
public void bar(Base base) {
if(handledClass.isAssignableFrom(base.getClass())) {
service.service(handledClass.cast(base)); // compile error at this line
}
}
}
// the following classes provide concrete implementations and the concrete class to perform the casting.
public static class RealDerived extends Derived{}
public static class FalseDerived extends Derived{}
public static class ConcreteService extends AbstractService<RealDerived>{
}
public static class ConcreteBarrer extends Barrer<RealDerived> {
protected ConcreteBarrer() {
super(RealDerived.class,new ConcreteService());
}
}
}