在 Java 中,我能否重用接口参数中的泛型类型来创建不同的 class,它也需要泛型类型?如果是这样,怎么办?
In Java, can I reuse the generics types from an interface parameter to create a different class which also requires generic types? And if so, how?
这是我拥有的 Java 代码的概述:
// An interface and an implementation class:
public interface MyInterface<T1, T2> { ... }
public class MyImplementation implements MyInterface<int, String> { ... }
// Another class
public class MyClass<T3, T4> { ... }
// Function I want to call
void doStuff(MyInterface i) {
MyClass<int, String> n;
}
// I want to call the function like this:
MyInterface mi = new MyImplementation();
doStuff(mi);
我想不通的是,我是否可以让 MyClass<int, String> n;
以某种方式使用来自 MyImplementation
class 的泛型类型传递给 doStuff()
?在这种情况下,n
会自动使用 <int, String>
因为那是 MyImplementation
使用的。
是的,你可以。
让我们抛开模糊的假设,采用真实的 类:Collection<T>
、Map<K, V>
和 Function<F, T>
。假设你想在 Map 类型(或接口,无所谓,签名就是签名)中编写一个方法,它接受一个 'key converter' (将 Ks 转换成其他东西的东西),返回一个集合something-else,它由映射中的每个键组成,通过转换器抛出并添加到集合中。
class MapImpl<K, V> implements Map<K, V> {
public <T> Collection<T> convertKeys(Function<K, T> converter) {
List<T> out = new ArrayList<T>();
for (K key : keySet()) out.add(converter.apply(key));
return out;
}
}
这里用到了很多概念:
- 该实现不锁定 K 和 V 的类型。您不只是从您实现的接口继承类型变量,因此,MapImpl 获得了它自己的 K、V,它也用作 K、V界面。涵盖第 1 行。
- convertKeys 方法除了已经获得的 K,V 之外,还引入了自己独特的类型变量。那是因为.. 好吧,这就是该方法的工作原理:地图的键具有某种类型,值具有某种其他类型,并且此转换器转换为某种第三种类型。三种类型:K、V 和 T。一个方法可以仅为该方法引入新的变量,这就是第 2 行中
<T>
的全部内容。
- 任何时候你命名一个类型名称,如果该类型是泛型的,你必须在它后面扔
<>
并放入适当的东西。或者不要放入适当的东西,这意味着:嘿,编译器,如果可以的话,请弄清楚(所谓的菱形运算符)。在您的代码片段中,您使用 MyInterface i
作为方法参数类型,这很糟糕:MyInterface 具有泛型,因此它后面必须有 <>
。在这种情况下,您必须添加一些东西,因为编译器无法尝试解决问题。
回到你的代码,它可能看起来像:
public <K, V> void doStuff(MyInterface<K, V> i) {
MyClass<K, V> n;
}
注意:记住,泛型 link 东西。最后一个片段只是说: 'i' 参数类型的 MyInterface 部分的第一个类型参数和 'n' 局部变量的 MyClass 部分的第一个类型参数之间有一个 link .我不知道那是什么类型。我知道它是同一类型。除非 typevar 出现在 2 个或更多地方,否则泛型是完全无用的。
NB2:如果你想要真正的花哨,你会开始考虑co/contra/invariance。例如,在关键转换器的故事中,如果你有一个转换器可以将任何对象转换成其他东西,那也很酷。事实上,一个可以转换 Ks 或 Ks 的任何超类型的转换器都是合适的。所以,真的,你最终得到:public <T> Collection<T> convertKeys(Function<? super K, ? extends T> converter) {}
- 但那种高级方差工程是一个很好的奖励,随意跳过源代码中的那些位,直到你 运行 陷入麻烦,因为你没有考虑一下。
这是我拥有的 Java 代码的概述:
// An interface and an implementation class:
public interface MyInterface<T1, T2> { ... }
public class MyImplementation implements MyInterface<int, String> { ... }
// Another class
public class MyClass<T3, T4> { ... }
// Function I want to call
void doStuff(MyInterface i) {
MyClass<int, String> n;
}
// I want to call the function like this:
MyInterface mi = new MyImplementation();
doStuff(mi);
我想不通的是,我是否可以让 MyClass<int, String> n;
以某种方式使用来自 MyImplementation
class 的泛型类型传递给 doStuff()
?在这种情况下,n
会自动使用 <int, String>
因为那是 MyImplementation
使用的。
是的,你可以。
让我们抛开模糊的假设,采用真实的 类:Collection<T>
、Map<K, V>
和 Function<F, T>
。假设你想在 Map 类型(或接口,无所谓,签名就是签名)中编写一个方法,它接受一个 'key converter' (将 Ks 转换成其他东西的东西),返回一个集合something-else,它由映射中的每个键组成,通过转换器抛出并添加到集合中。
class MapImpl<K, V> implements Map<K, V> {
public <T> Collection<T> convertKeys(Function<K, T> converter) {
List<T> out = new ArrayList<T>();
for (K key : keySet()) out.add(converter.apply(key));
return out;
}
}
这里用到了很多概念:
- 该实现不锁定 K 和 V 的类型。您不只是从您实现的接口继承类型变量,因此,MapImpl 获得了它自己的 K、V,它也用作 K、V界面。涵盖第 1 行。
- convertKeys 方法除了已经获得的 K,V 之外,还引入了自己独特的类型变量。那是因为.. 好吧,这就是该方法的工作原理:地图的键具有某种类型,值具有某种其他类型,并且此转换器转换为某种第三种类型。三种类型:K、V 和 T。一个方法可以仅为该方法引入新的变量,这就是第 2 行中
<T>
的全部内容。 - 任何时候你命名一个类型名称,如果该类型是泛型的,你必须在它后面扔
<>
并放入适当的东西。或者不要放入适当的东西,这意味着:嘿,编译器,如果可以的话,请弄清楚(所谓的菱形运算符)。在您的代码片段中,您使用MyInterface i
作为方法参数类型,这很糟糕:MyInterface 具有泛型,因此它后面必须有<>
。在这种情况下,您必须添加一些东西,因为编译器无法尝试解决问题。
回到你的代码,它可能看起来像:
public <K, V> void doStuff(MyInterface<K, V> i) {
MyClass<K, V> n;
}
注意:记住,泛型 link 东西。最后一个片段只是说: 'i' 参数类型的 MyInterface 部分的第一个类型参数和 'n' 局部变量的 MyClass 部分的第一个类型参数之间有一个 link .我不知道那是什么类型。我知道它是同一类型。除非 typevar 出现在 2 个或更多地方,否则泛型是完全无用的。
NB2:如果你想要真正的花哨,你会开始考虑co/contra/invariance。例如,在关键转换器的故事中,如果你有一个转换器可以将任何对象转换成其他东西,那也很酷。事实上,一个可以转换 Ks 或 Ks 的任何超类型的转换器都是合适的。所以,真的,你最终得到:public <T> Collection<T> convertKeys(Function<? super K, ? extends T> converter) {}
- 但那种高级方差工程是一个很好的奖励,随意跳过源代码中的那些位,直到你 运行 陷入麻烦,因为你没有考虑一下。