【问题标题】:Java Generics restrict to interfaceJava 泛型限制为接口
【发布时间】:2013-07-12 19:41:15
【问题描述】:

我不确定这是否可能,但我想要完成的是:

public static <A,B extends SomeClass & A> B makeB(A thing) {...}

基本上,使用反射/生成驱动的过程,我想提供 B 类型的东西,其中 B 属于 SomeClass 类并实现接口 A,而 A 是用户通过泛型提供的。

我不是在询问生成 B 的机制——我已经控制了它。我正在寻找一种将泛型类型参数&lt;A&gt; 限制为接口而不是类的方法,以便我可以使用语法B extends SomeClass &amp; A 来实现干净的类型安全。

这可能吗?有人知道解决此问题的替代方法吗?


编辑:我想我表达的不是很清楚,因为它似乎在 cmets 中引起了混乱:

B 旨在作为通配符的占位符,以便客户端可以获得一个既是 SomeClass 又是 A 的对象,而无需基于信任进行强制转换。客户端将无法访问实现SomeClassA 的实际类的名称,因为它是在编译时生成的,因此存在关于类型安全的问题。

【问题讨论】:

  • 不,不可能。
  • 这让我一脸悲伤:(
  • 另一方面,如果你想要一个反射驱动的东西,你可以传递一个Class&lt;A&gt;并显式调用Class.isInterface()
  • @LouisWasserman 他已经有一个 A 的实例作为参数。他可以从中获得 Class 。他不需要其他参数。
  • @jahroy 抱歉,直到您删除答案后,我才有机会看到您对我的评论。你关于推断B 的观点是绝对正确的——我实际上在编辑我的答案时提到了类似的问题。您绝对应该取消删除您的答案,因为它很有帮助。

标签: java generics interface


【解决方案1】:

不可能强加这样的编译时限制。泛型类型参数是引用类型的替身;它们不区分类类型和接口类型。类型参数声明中的附加边界必须是接口类型这一事实只是偶然的——您利用它作为将类型归为接口的手段的策略很聪明,但它被类型参数can't be used in multiple bounds 的限制所击败。

您唯一的选择是像 Louis Wasserman pointed out 一样使用 Class.isInterface() 进行运行时检查,或者让调用者对其传入的内容负责。无论哪种方式,请确保清楚地记录方法的期望和行为。


B 旨在用作通配符的占位符,以便客户端可以获得一个既是 SomeClass 又是 A 的对象,而无需基于信任进行强制转换。客户端将无法访问实现SomeClassA 的实际类的名称

这对我来说似乎很矛盾。如果调用者不可能知道它的评估结果是什么,那么声明 B 是没有意义的。请记住:泛型方法的调用者提供其类型参数。因此,调用者在没有任何依据的情况下决定 B 只能是猜测 - 这绝不可能是类型安全的。

看起来您真正希望您的方法返回的类型是SomeClassA,但这很棘手,因为它们不共享一个共同的超类型:

public static <A> SomeClass&A makeSomeClass(A thing) {...}

(这是nonsensical syntax,仅用于演示)

作为一种解决方法,请考虑表示SomeClass 和某些接口类型的替代方法。例如,候选接口可以有一个通用的方法来返回SomeClass

public interface IsSomeClass {
    SomeClass asSomeClass();
}

public interface Foo extends IsSomeClass { }

asSomeClass 的实现实际上只会返回 this。然后你可以这样做:

public static <A extends IsSomeClass> A makeSomeClass(Class<A> type) {...}

并且该方法的调用者可以将返回的对象用作任一类型:

final Foo foo = makeSomeClass(Foo.class);
final SomeClass someClass = foo.asSomeClass();

如果接口本身不能修改,那么另一种选择是使用包装类和组合:

final class SomeClassWrapper<A> {

    private final SomeClass someClass;
    private final A a;

    //constructor and getters, etc.
}

您的方法将返回一个包装器实例,将实现实例分配给两个someClassa

public static <A> SomeClassWrapper<A> makeSomeClass(Class<A> type) {...}

【讨论】:

  • 感谢您的回答。
【解决方案2】:

如果SomeClass始终是一个类,那么&lt;B extends SomeClass &amp; A&gt;中的A只能是一个接口,因为Java中没有多重继承。满足&amp; A 的唯一方法是A 是一个接口。

【讨论】:

  • 你可能会被忽略,但是编译器会抱怨:A 类型不是接口;不能指定为有界参数
  • @jahroy 和@EJP 你似乎忽略了A&lt;A,B extends SomeClass &amp; A&gt; 中的一个类型参数,这就是它无法编译的原因。
  • 是的...你是对的...但是没有办法告诉编译器泛型参数将是一个接口?
  • @ZoltánNagy 这就是要点。如果作为A 提供的实际类型参数不是编译器会抱怨的接口。
  • @ZoltánNagy - 泛型的意义在于让编译器帮助您实施类型安全。如果您希望 A 成为接口并且编译器在不是接口时抱怨,那么泛型正在完成他们的工作。
【解决方案3】:

我认为这里的问题是你想从这个方法返回一个B

您将B 指定为类型参数,但它不会出现在方法签名的其他任何地方。

编译器应该如何从参数推断返回类型????

客户端代码没有机会指定B 是什么。

您似乎应该返回SomeClassA

其中任何一个都可以是底层的B,但在客户端代码中应该显示为SomeClassA

【讨论】:

    猜你喜欢
    • 2021-10-03
    • 1970-01-01
    • 2018-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    相关资源
    最近更新 更多