Java 通用接口与通用方法,以及何时使用它们
Java Generic Interface vs Generic Methods, and when to use one
我想知道,除了语法差异之外,什么时候会在接受通用参数的方法上使用通用接口?
public interface Flight<T>{
void fly(T obj);
}
超过
public interface Flight{
void <T> fly(T obj);
}
当您期望实现中的大多数方法将对实例化 class 时提供的类型执行操作时,您应该使用泛型类型。
例如,ArrayList<E>
是一种通用类型,因为它的大部分操作(添加、获取、删除等)都依赖于创建一个时指定的类型。
当 class 中只有少数方法依赖于不同类型时,应使用通用方法。
您可以在 Java Docs 中阅读有关泛型的更多信息。
如果您声明一个泛型 方法 ,您总是让 调用者 决定将哪种类型参数用于类型参数。该方法的实现必须能够处理所有可能的类型参数(它甚至没有办法请求实际的类型参数)。
就是说,像 <T> void fly(T obj);
这样的方法声明调用者可以为 T
使用任何类型,而实现唯一可以依赖的是 T
的实际类型将可分配给 Object
(就像 <T extends Object>
已声明)。
所以在这个具体的例子中,它与声明void fly(Object obj);
没有什么不同,它也允许任意对象。
相比之下,interface
上的类型参数是契约的一部分,可以由 实现[=58] 指定或限制=] 的 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) {
}
}
仍然是通用的,但限制类型。
当 interface
本身是另一个方法签名的一部分时,interface
的签名也很重要,例如
public void foo(Flight<? super String> f) {
f.fly("some string value");
}
此处,传递给 foo
的 Flight
实现必须能够使用 String
值,因此 Flight<String>
或 Flight<CharSequence>
或 Flight<Object>
就足够了,但 Flight<Integer>
就不够了。声明这样的合同需要 interface
上的类型参数,而不是 interface
的方法。
以class java.util.ArrayList<E>
为例。创建该类型的变量时,必须为 T
:
指定具体类型
ArrayList<String> list = new ArrayList<>();
当从 List
接口调用方法时,使用这些具体类型,它们与类型 T
一起工作。调用add
方法,只能将String
个对象添加到列表中。使用 get
从列表中检索元素,您将获得具体类型 String
.
的元素
对于泛型方法,仅为此方法指定类型 T
。如果方法 returns 是该泛型类型的值,那将更有意义。您经常会发现这样的代码:
MyObject obj = SomeClass.staticGenericMethod(MyObject.class)
或
MyObject obj = classInstance.genericMethod(MyObject.class);
您的接口名称应该以大写字母开头:Flight<T>
我想知道,除了语法差异之外,什么时候会在接受通用参数的方法上使用通用接口?
public interface Flight<T>{
void fly(T obj);
}
超过
public interface Flight{
void <T> fly(T obj);
}
当您期望实现中的大多数方法将对实例化 class 时提供的类型执行操作时,您应该使用泛型类型。
例如,ArrayList<E>
是一种通用类型,因为它的大部分操作(添加、获取、删除等)都依赖于创建一个时指定的类型。
当 class 中只有少数方法依赖于不同类型时,应使用通用方法。
您可以在 Java Docs 中阅读有关泛型的更多信息。
如果您声明一个泛型 方法 ,您总是让 调用者 决定将哪种类型参数用于类型参数。该方法的实现必须能够处理所有可能的类型参数(它甚至没有办法请求实际的类型参数)。
就是说,像 <T> void fly(T obj);
这样的方法声明调用者可以为 T
使用任何类型,而实现唯一可以依赖的是 T
的实际类型将可分配给 Object
(就像 <T extends Object>
已声明)。
所以在这个具体的例子中,它与声明void fly(Object obj);
没有什么不同,它也允许任意对象。
相比之下,interface
上的类型参数是契约的一部分,可以由 实现[=58] 指定或限制=] 的 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) {
}
}
仍然是通用的,但限制类型。
当 interface
本身是另一个方法签名的一部分时,interface
的签名也很重要,例如
public void foo(Flight<? super String> f) {
f.fly("some string value");
}
此处,传递给 foo
的 Flight
实现必须能够使用 String
值,因此 Flight<String>
或 Flight<CharSequence>
或 Flight<Object>
就足够了,但 Flight<Integer>
就不够了。声明这样的合同需要 interface
上的类型参数,而不是 interface
的方法。
以class java.util.ArrayList<E>
为例。创建该类型的变量时,必须为 T
:
ArrayList<String> list = new ArrayList<>();
当从 List
接口调用方法时,使用这些具体类型,它们与类型 T
一起工作。调用add
方法,只能将String
个对象添加到列表中。使用 get
从列表中检索元素,您将获得具体类型 String
.
对于泛型方法,仅为此方法指定类型 T
。如果方法 returns 是该泛型类型的值,那将更有意义。您经常会发现这样的代码:
MyObject obj = SomeClass.staticGenericMethod(MyObject.class)
或
MyObject obj = classInstance.genericMethod(MyObject.class);
您的接口名称应该以大写字母开头:Flight<T>