Java泛型接口和泛型方法,何时使用哪个?

24

除了语法上的差异,什么情况下会使用通用接口而不是接受通用参数的方法?

public interface Flight<T>{
   void fly(T obj);
}

结束

public interface Flight{
    void <T> fly(T obj);
}

2
你想让实现'flight'的一个实例在'fly'方法中只能处理一种选定类型的数据还是任何类型的数据? - Pshemo
3个回答

21
如果您声明了一个通用的方法,则始终应该让调用者决定使用哪些类型参数来使用。该方法的实现必须能够处理所有可能的类型参数(它甚至没有一种方式来询问实际的类型参数)。
也就是说,像<T> void fly(T obj);这样的方法表示调用者可以使用任何类型作为T,而实现只能依赖于实际类型可以分配给Object(就像声明了<T extends Object>一样)。
因此,在这个具体的例子中,它与声明void fly(Object obj);并没有什么不同,后者也允许任意对象。
相比之下,接口上的类型参数是合同的一部分,并且可以由实现interface指定或限制:
public interface Flight<T>{
   void fly(T obj);
}

允许像这样的实现:

public class X implements Flight<String> {
   public void fly(String obj) {
   }
}

在实现代码方面修复 T 的类型。或者
public class NumberFlight<N extends Number> implements Flight<N> {
   public void fly(N obj) {
   }
}

保持通用性但限制类型。


接口的签名在它作为另一个方法签名的一部分时也很重要,例如:

public void foo(Flight<? super String> f) {
    f.fly("some string value");
}

在这里,你需要传递给fooFlight实现必须能够消费一个String值,因此Flight<String>Flight<CharSequence>Flight<Object>是足够的,但不是Flight<Integer>。声明这样的合同需要在interface上使用类型参数,而不是在interface的方法上。


因此,在泛型方法上使用通用接口方法仍然归结为设计决策。增加泛型类型并不真正改变方法和接口的作用,尽管它增加了某些功能,比如类型限制。NumberFlight甚至可以在不改变Flight<T>之间的契约的情况下限制类型。那很有趣。我现在更加开明了,非常感谢! - yev

0

当您期望大多数实现方法在实例化类时执行的操作是基于提供的类型时,应使用通用类型。

例如,ArrayList<E> 是一种通用类型,因为它的大多数操作(添加、获取、删除等)依赖于创建时指定的类型。

当类中仅有少数方法依赖于不同类型时,应使用通用方法。

您可以在Java Docs中了解更多关于通用类型的信息。


0
拿Java中的java.util.ArrayList<E>作为例子。当你创建这个类型的变量时,必须指定T的具体类型:
ArrayList<String> list = new ArrayList<>();

当调用与类型 T 相关的 List 接口方法时,使用这些具体类型。当调用 add 方法时,只能将 String 对象添加到列表中。使用 get 从列表中检索元素时,您将获得具体类型为 String 的元素。

对于泛型方法,类型 T 仅针对该方法进行指定。如果方法返回该泛型类型的值,则更有意义。通常会发现如下代码:

MyObject obj = SomeClass.staticGenericMethod(MyObject.class)

或者

MyObject obj = classInstance.genericMethod(MyObject.class);

你应该以大写字母开头命名你的接口名称:Flight<T>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接