在 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) {} - 但那种高级方差工程是一个很好的奖励,随意跳过源代码中的那些位,直到你 运行 陷入麻烦,因为你没有考虑一下。