理解 Java 中的泛型

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

Understanding generic in Java

问题

以下是翻译好的内容:

我正在构建一些通用代码用于返回任何类型的对象集合这是我的代码

public class Util{
    public static <T, R extends Collection> R a(Collection<T> collection) {
        return (R) collection;
    }

    public static <T> List<T> listWith(T... items) {
        return Arrays.asList(items);
    }

    public static <T> Set<T> setWith(T... items) {
        return new HashSet<>(Arrays.asList(items));
    }
}

我的想法是通过这个Util类我可以创建任何类型的列表或集合就像这样

List<String> list = a(listWith("str1", "str2"));
Set<Object> set = a(setWith(new Object(), new Object()));

我现在有两个问题
1. 如何限制左边的Obj类型与传递给`listWith`方法的参数的Obj类型相同因为现在我可以做类似这样的事情:`Set<String> set = a(setWith(new Object(), new Object()));` 而没有编译错误
2. 如何限制`a`方法的返回类型仅为**扩展Collection的类**现在我可以做类似这样的事情:`User user = a(listWith("test"));` 也没有编译错误但然后它会引发一个转型异常我尝试过将返回类型定义为`R extends Collection`,但没有成功
英文:

I am building some generic codes which returning a collection of any type of object. Here is my build:

public class Util{
    public static &lt;T, R extends Collection&gt; R a(Collection&lt;T&gt; collection) {
        return (R) collection;
    }

    public static &lt;T&gt; List&lt;T&gt; listWith(T... items) {
        return Arrays.asList(items);
    }

    public static &lt;T&gt; Set&lt;T&gt; setWith(T... items) {
        return new HashSet&lt;&gt;(Arrays.asList(items));
    }
}

My Idea is from this Util class I can create any list or set of any object type, something like:

List&lt;String&gt; list = a(listWith(&quot;str1&quot;, &quot;str2&quot;));
Set&lt;Object&gt; set = a(setWith(new Object(), new Object()));

I have now two questions:

  1. How can I restrict the type of the Obj in the left to be the same as the Obj type of the parameter passed to the listWith method? Because right now I can do something like : Set&lt;String&gt; set = a(setWith(new Object(), new Object())); without any compile error
  2. How can I restrict the return type of the a method to be only class that extends the Collection. Right now I can do something like: User user = a(listWith(&quot;test&quot;)); also without compile error but then it will throw an casting exception. I did try above with return type as R extends Collection but it did not success

答案1

得分: 2

  1. 如何限制左侧的 Obj 类型与传递给 listWith 方法的参数的 Obj 类型相同?

    不要使用 Collection 的原始类型,而是将其限制为泛型类型。

    public static <T, R extends Collection<T>> R a(Collection<T> collection) { 
        return (R) collection;
    }
    

    但是可以进行更好的设计(请参阅下文)。

  2. 如何限制 a 方法的返回类型仅为扩展 Collection 的类。目前我可以执行类似以下的操作:User user = a(listWith("test")); 即使没有编译错误,但随后会抛出转换异常。

    请记住类 User 未来可能会实现 Collection,编译器在编译时不会检查此信息。

    进一步的限制如下。如果 R 是 Collection 的子类型,并且需要同一集合作为输入参数,则可以要求 R collection,并且无需显式转换即可返回。

     public static <T, R extends Collection<T>> R a(R collection) {
         return collection;
     }
    

应用答案的第二部分后:

List<String> list = a(listWith("str1", "str2"));           // 编译通过
Set<Object> set = a(setWith(new Object(), new Object()));  // 编译通过

Set<String> set2 = a(setWith(new Object(), new Object())); // 无法编译
User user = a(listWith("test"));                           // 无法编译

这种限制对于这个简单的情况是有效的,但完全没有意义。您可以在 @DmytroMitin 的 答案 中找到进一步的解释。

正如您所看到的,整个 #2 的应用最终 返回输入本身,这使我产生了一个问题:

“是否需要这种泛型包装?”

List<String> list = listWith("str1", "str2");
Set<Object> set = setWith(new Object(), new Object());
英文:
  1. > How can I restrict the type of the Obj in the left to be the same as the Obj type of the parameter passed to the listWith method?

    Don't use a raw type of the Collection and restrict it to the generic type.

    public static &lt;T, R extends Collection&lt;T&gt;&gt; R a(Collection&lt;T&gt; collection) { 
        return (R) collection;
    }
    

    But it can be designed better (look below).

  2. > How can I restrict the return type of the a method to be only class that extends the Collection. Right now I can do something like: User user = a(listWith("test")); also without compile error but then it will throw an casting exception.

    Remember the class User might implement Collection in the future and compiler doesn't check this information at the compile time.

    Further restriction will do that. If the R is a subtype of Collection&lt;T&gt; and the very same collection is required as an input parameter, you can ask for R collection and return it without explicit casting.

     public static &lt;T, R extends Collection&lt;T&gt;&gt; R a(R collection) {
         return collection;
     }
    

After applying the 2nd part of the answer:

List&lt;String&gt; list = a(listWith(&quot;str1&quot;, &quot;str2&quot;));           // compiles
Set&lt;Object&gt; set = a(setWith(new Object(), new Object()));  // compiles

Set&lt;String&gt; set2 = a(setWith(new Object(), new Object())); // doesn&#39;t compile
User user = a(listWith(&quot;test&quot;));                           // doesn&#39;t compile

This restriction is valid for this simple case but doesn't make sense at all. You can find a further explanation at @DmytroMitin's answer.

As you can see, the whole application of the #2 finally returns the input itself, which it gives me a question:

"Is such generic wrapping needed?":

List&lt;String&gt; list = listWith(&quot;str1&quot;, &quot;str2&quot;);
Set&lt;Object&gt; set = setWith(new Object(), new Object());

答案2

得分: 2

我只会翻译你提供的代码部分,不会回答关于翻译的问题。

我会补充**@Nikolas**的答案解释他的修复方法 #1

public static <T, R extends Collection<T>> R a(Collection<T> collection) { 
    return (R) collection;
}

User user = a(listWith("test")); // 仍然可以编译,即为何 `R extends Collection<T>` 没有足够限制返回类型,使其仍然可以是 `User`。

我猜问题在于方法 `a` 的类型参数 `T, R` 可以被推断为

User user = Util.<String, User & Collection<String>>a(listWith("test"));

这里 `Util.<String, User & Collection<String>>a...` 似乎不是有效的语法但如果我们定义

class User {}
class SubUser extends User implements Collection<String> { ... }

那么

User user = Util.<String, SubUser>a(listWith("test"));

是可能的

---

关于你的第二个问题

> 我如何限制 `a` 方法的返回类型只能是继承了 Collection 的类

一般情况下这似乎是不可能的因为对于每个类 `User`,它的子类

class SubUser<T> extends User implements Collection<T> {...}

仍然是可能的
英文:

I'll just add to @Nikolas's answer why with his fix #1

public static &lt;T, R extends Collection&lt;T&gt;&gt; R a(Collection&lt;T&gt; collection) { 
    return (R) collection;
}

User user = a(listWith(&quot;test&quot;)); still compiles i.e. why R extends Collection&lt;T&gt; doesn't restrict enough the return type so that it still can be User.

I guess the thing is that the type parameters T, R of the method a can be inferred like

User user = Util.&lt;String, User &amp; Collection&lt;String&gt;&gt;a(listWith(&quot;test&quot;));

Here Util.&lt;String, User &amp; Collection&lt;String&gt;&gt;a... seems to be not valid syntax but if we define

class User {}
class SubUser extends User implements Collection&lt;String&gt;{ ... }

then

User user = Util.&lt;String, SubUser&gt;a(listWith(&quot;test&quot;));

is possible.


Regarding your 2nd question

> How can I restrict the return type of the a method to be only class that extends the Collection.

Generally this seems impossible because for every class User its subclass

class SubUser&lt;T&gt; extends User implements Collection&lt;T&gt; {...}

is still possible.

huangapple
  • 本文由 发表于 2020年9月16日 20:33:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63920194.html
匿名

发表评论

匿名网友

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

确定