I have a strange problem with the Java compiler. Here is the code:
private <T extends IdentifiedBusinessTransversalEntity> T
getOrCreateTransversalEntity(Class<T> classT, String id) {
...}
private <T extends IdentifiedBusinessDSEntity> T
getOrCreateDSEntity(Class<T> classT, String id) {
...}
public abstract class IdentifiedBusinessDSEntity extends
BusinessDSEntity implements IdentifiedEntity {
...}
public abstract class IdentifiedBusinessTransversalEntity extends
BusinessTransversalEntity implements IdentifiedEntity {
...}
public <T extends IdentifiedEntity> T getOrCreate(Class<T> classT, String id)
{
if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT))
{
return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class),id);
} else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT))
{ //must cast explicitly to T here but works well just above and is exactly the same. Strange
return (T) getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class),id);
}
return null;
}
I don't understand why in the getOrCreate
function the compiler allows the first return without (T)
but not in the second return. The error is:
Type mismatch: cannot convert from IdentifiedBusinessTransversalEntity
to T
It should be ok: getOrCreateTRansversalEntity
returns a subclass of IdentifiedBusinessTransversalEntity
which implements IdentifiedEntity
.
The strangest is that it is ok for the first return and this is exactly symmetrical. What is the difference?
答案 0 :(得分:1)
该代码不应该编译。
我相信你要么犯了一个错误,导致你相信它有效,或者你的编译器中有错误。 getOrCreate
中的两个return语句都为Eclipse 4.5.1和javac 1.8.0_45中的编译错误。
编辑: 我改变了这个解释,我想我一下子就误解了这个问题。
在解释中,我将getAndCreate
的类型参数的名称更改为C
,以避免与其他类型参数混淆。
问题在于asSubclass
方法中,该类是C
的子类的信息丢失了;剩下的唯一信息是该类是例如IdentifiedBusinessDSEntity
的子类。
asSubclass
具有以下类型:
<U> Class<? extends U> asSubclass(Class<U> clazz)
我们可以看到接收器类的原始类型参数T
在返回类型中不存在。
getOrCreate
返回C
。这就是您需要强制转换的原因:要将类型C
重新引入返回值。
asSubclass
我们可以想象asSubclass
具有以下类型:
<U> Class<? extends U & T> asSubclass(Class<U> clazz)
这将是类型安全的,并且使用该返回类型,您的代码将在没有强制转换的情况下编译。但是Java中不允许使用像这样的边界的多个类型参数。
以下是我用来调查问题的代码:
class Test {
interface IdentifiedEntity {}
class BusinessDSEntity {}
class BusinessTransversalEntity {}
private <T extends IdentifiedBusinessTransversalEntity> T getOrCreateTransversalEntity(Class<T> classT, String id) {
return null;
}
private <T extends IdentifiedBusinessDSEntity> T getOrCreateDSEntity(Class<T> classT, String id) {
return null;
}
public abstract class IdentifiedBusinessDSEntity
extends BusinessDSEntity implements IdentifiedEntity {}
public abstract class IdentifiedBusinessTransversalEntity
extends BusinessTransversalEntity implements IdentifiedEntity {}
public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) {
if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) {
// Error here. Note that the type of s does not contain C.
Class<? extends IdentifiedBusinessDSEntity> s = classT.asSubclass(IdentifiedBusinessDSEntity.class);
return getOrCreateDSEntity(s, id);
} else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) {
// Also error here
return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), id);
}
return null;
}
}
我无法想到任何保留多种类型安全性的优雅解决方案。
一种替代方法是将返回类型的类传递给特定于实体的方法。这是相当丑陋和不方便的,你必须手动验证该类真的是一个合适的类型。
即使丢失了某些类型的安全性,最好还是投出返回值。
示例:
class Test {
interface IdentifiedEntity {}
class BusinessDSEntity {}
class BusinessTransversalEntity {}
private <R extends IdentifiedEntity, T extends IdentifiedBusinessDSEntity>
R getOrCreateDSEntity(Class<T> classT, Class<R> classR, String id)
{
// Verify that classT really is subclass of classR.
classT.asSubclass(classR);
return null;
}
private <R extends IdentifiedEntity, T extends IdentifiedBusinessTransversalEntity>
R getOrCreateTransversalEntity(Class<T> classT, Class<R> classR, String id)
{
// Verify that classT really is subclass of classR.
classT.asSubclass(classR);
return null;
}
public abstract class IdentifiedBusinessDSEntity
extends BusinessDSEntity implements IdentifiedEntity {}
public abstract class IdentifiedBusinessTransversalEntity
extends BusinessTransversalEntity implements IdentifiedEntity {}
public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) {
if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) {
return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class), classT, id);
} else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) {
return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), classT, id);
}
return null;
}
}
答案 1 :(得分:0)
我会试一试。这种解释不仅仅基于观察而非显性知识,因此如果我得到纠正,不要感到惊讶。我认为你感到困惑的是<T extends IdentifiedBusinessDSEntity> T
返回类型声明的含义。在我看来,这个返回声明是创建一个全新的类型,而不是描述已经声明的类型。换句话说,您可以将Class1作为Class1 extends ClassA
,但是当您返回T extends ClassA
时,您不描述Class1,而是一个全新的类(或类型)。你不能指望这个新类与Class1相同,而是说你返回的是一个ClassX ClassX extends ClassA
:Class1和ClassX不是同一个类。
看起来您正在尝试使用缓存实现Factory Design模式。这段代码就是这样做的,应该与你上面做的很相似。假设工厂应该生成接口实现,则不需要将返回类型声明为扩展接口的类,只需返回接口。
public class Factory {
interface I {}
List<I> iCache = new ArrayList<I>();
abstract class ClassA {}
abstract class ClassB {}
class Class1 extends ClassA implements I {}
class Class2 extends ClassB implements I {}
I getOrCreateTypeA() {
for( I cls: iCache ) {
if( cls instanceof ClassA ) return cls;
}
Class1 cls = new Class1();
iCache.add(cls);
return cls;
}
I getOrCreateTypeB() {
for( I cls: iCache ) {
if( cls instanceof ClassB ) return cls;
}
Class2 cls = new Class2();
iCache.add(cls);
return cls;
}
I getOrCreate(Class<?> cls) {
if ( ClassA.class.isAssignableFrom(cls)) {
return getOrCreateTypeA();
} else if ( ClassB.class.isAssignableFrom(cls)) {
return getOrCreateTypeB();
}
return null;
}
void run() {
I classI1 = getOrCreate(Class1.class);
System.out.println(classI1);
I classI2 = getOrCreate(Class2.class);
System.out.println(classI2);
I classI3 = getOrCreate(Class1.class);
System.out.println(classI3);
System.out.println(iCache);
}
public static void main(String... args) {
new Factory().run();
}
}