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?

huangapple go评论111阅读模式
英文:

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代码概述:

// 一个接口和一个实现类:
public interface MyInterface<T1, T2> { ... }
public class MyImplementation implements MyInterface<int, String> { ... }

// 另一个类
public class MyClass<T3, T4> { ... }

// 我想要调用的函数
void doStuff(MyInterface i) {
  MyClass<int, String> n;
}

// 我想要这样调用函数:
MyInterface mi = new MyImplementation();
doStuff(mi);

我不能确定的是,是否可以使 MyClass<int, String> n; 在某种程度上使用从传递给 doStuff()MyImplementation 类中获取的泛型类型?在这种情况下,n 将自动使用 <int, String>,因为这是 MyImplementation 使用的内容。

英文:

Here is an overview of the Java code I have:

// An interface and an implementation class:
public interface MyInterface&lt;T1, T2&gt; { ... }
public class MyImplementation implements MyInterface&lt;int, String&gt; { ... }

// Another class
public class MyClass&lt;T3, T4&gt; { ... }

// Function I want to call
void doStuff(MyInterface i) {
  MyClass&lt;int, String&gt; n;
}

// I want to call the function like this:
MyInterface mi = new MyImplementation();
doStuff(mi);

What I can't figure out is if I can get MyClass&lt;int, String&gt; n; to somehow use the generic types from the MyImplementation class passed in to doStuff()? In this case, n would automatically use &lt;int, String&gt; because that's what MyImplementation uses.

答案1

得分: 1

可以的。

让我们远离模糊的假设,来看看真实的类:Collection<T>Map<K, V>Function<F, T>。假设你想要在Map类型(或接口,无所谓,签名就是签名)中编写一个方法,该方法接受一个'键转换器'(一种将K转换为其他类型的东西),返回一个由这些其他类型元素组成的集合,其中包括映射中的每个键,经过转换器转换后添加到集合中。

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;
}

注意:泛型将事物联系在一起。那个最后的片段只是在说:i参数类型的MyInterface部分的第一个类型参数与n局部变量的MyClass部分的第一个类型参数之间有一个链接。我不知道那个类型是什么。我知道它是相同的类型。除非类型变量在2个或更多的位置出现,否则泛型完全没有用。

注意2:如果你想要变得更加高级,你可以考虑协变/逆变/不变性。例如,在键转换器的故事中,如果你有一个可以将任何对象转换为其他类型的转换器,那也很酷。实际上,一个可以将K或任何K的超类型转换的转换器也是合适的。所以,实际上,你最终会得到:public <T> Collection<T> convertKeys(Function<? super K, ? extends T> converter) {} - 但是这种高级的变异工程只是一个很好的奖励,如果你没有考虑到它而导致出问题,可以随意忽略源代码中的这些部分。

英文:

Yes, you can.

Let's move away from nebulous hypotheticals and take real classes: Collection&lt;T&gt;, Map&lt;K, V&gt;, and Function&lt;F, T&gt;. Let's say you want to write a method in the Map type (or interface, doesn't matter, a signature is a signature) that takes a 'key converter' (a thing that converts Ks into something else), returning a collection of the something-else, which consists of each key in the map, thrown through the converter, and added to a collection.

class MapImpl&lt;K, V&gt; implements Map&lt;K, V&gt; {
    public &lt;T&gt; Collection&lt;T&gt; convertKeys(Function&lt;K, T&gt; converter) {
        List&lt;T&gt; out = new ArrayList&lt;T&gt;();
        for (K key : keySet()) out.add(converter.apply(key));
        return out;
    }
}

A lot of concepts are being used here:

  • The implementation doesn't lock in the types of K and V. You don't just inherit typevars from interfaces you implement, so, MapImpl gets its own K,V which are also used as the K,V for the interface. That covers line 1.
  • The convertKeys method introduces its own unique typevar, in addition to the K,V it already gets. That's because.. well, that's how the method works: The map's keys have some type, the values have some other type, and this converter converts to some third type. Three types: K, V, and T. A method can introduce new vars just for the method, that's what the &lt;T&gt; is all about in line 2.
  • Any time you name a type name, if that type is generified, you MUST toss &lt;&gt; after it and put in appropriate things. Or don't put in appropriate things which means: Hey, compiler, figure it out if you can (the so called diamond operator). In your snippet, you use MyInterface i as method param type and that's bad: MyInterface has generics, so it must have &lt;&gt; behind it. In this case, you have to add things because there is no way the compiler can try to figure things out.

Going back to your code, it might look like:

public &lt;K, V&gt; void doStuff(MyInterface&lt;K, V&gt; i) {
    MyClass&lt;K, V&gt; n;
}

NB: Remember, generics link things. That final snippet is simply saying: There is a link between the first typearg of the MyInterface part of the 'i' parameter's type, and the first typearg of the MyClass part of the 'n' local variable. I don't know what that type is. I do know it is the same type. Generics are completely useless unless the typevar occurs in 2 or more places.

NB2: If you then want to get real fancy, you start thinking about co/contra/invariance. For example, in the key converter story, if you have a converter that can convert any object into something else that'd be cool too. In fact, a converter that can convert either Ks, or any supertype of Ks, that'd all be suitable. So, really, you end up with: public &lt;T&gt; Collection&lt;T&gt; convertKeys(Function&lt;? super K, ? extends T&gt; converter) {} - but that kind of advanced variance engineering is a nice bonus, feel free to skip those bits in your source until you run into trouble because you didn't take it into consideration.

huangapple
  • 本文由 发表于 2020年8月20日 22:04:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/63506848.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定