【问题标题】:What is the difference between ? and Object in Java generics?和有什么区别?和 Java 泛型中的对象?
【发布时间】:2010-10-15 07:06:40
【问题描述】:

我正在使用 Eclipse 来帮助我清理一些代码以正确使用 Java 泛型。大多数情况下,它在推断类型方面做得很好,但在某些情况下,推断的类型必须尽可能通用:对象。但是 Eclipse 似乎让我可以在 Object 类型和 '?' 类型之间进行选择。

那么有什么区别:

HashMap<String, ?> hash1;

HashMap<String, Object> hash2;

【问题讨论】:

  • Wildcards官方教程。它很好地解释了它,并举例说明了为什么它比简单地使用 Object 是必要的。
  • @BenS 您发布的链接指的是 oracle 文档的错误位置。 Type 和 Type> 之间的区别开始在 previous page 上进行解释

标签: java generics


【解决方案1】:

HashMap&lt;String, String&gt; 的实例匹配 Map&lt;String, ?&gt; 但不匹配 Map&lt;String, Object&gt;。假设你想写一个方法来接受从Strings 到任何东西的映射:如果你想写

public void foobar(Map<String, Object> ms) {
    ...
}

您不能提供HashMap&lt;String, String&gt;。如果你写

public void foobar(Map<String, ?> ms) {
    ...
}

有效!

在 Java 的泛型中有时会被误解的是 List&lt;String&gt; 不是 List&lt;Object&gt; 的子类型。 (但String[] 实际上是Object[] 的子类型,这就是泛型和数组不能很好混合的原因之一。(Java 中的数组是协变的,泛型不是,它们是不变的))。

示例: 如果您想编写一个接受Lists 的InputStreams 和InputStream 的子类型的方法,您可以编写

public void foobar(List<? extends InputStream> ms) {
    ...
}

顺便说一句:Joshua Bloch's Effective Java 是一个很好的资源,当您想了解 Java 中不那么简单的事情时。 (你上面的问题在书中也很好地涵盖了。)

【讨论】:

  • 这是在控制器级别上为我的所有控制器功能使用 ResponseEntity> 的正确方法吗?
  • Object 是所有 Java 类的超类型,但是 Object 的集合不是任何集合的超类型。例如,List 不是 List 的超类型,将 List 类型的变量分配给 List 类型的变量将导致编译器错误。
【解决方案2】:

考虑这个问题的另一种方式是

HashMap<String, ?> hash1;

等价于

HashMap<String, ? extends Object> hash1;

将此知识与来自 Java Generics and Collections 的第 (2.4) 节中的“获取和放置原则”结合起来:

Get 和 Put 原则:使用 当你只得到时扩展通配符 结构中的值,使用 super 仅将值放入时的通配符 结构,不要使用通配符 当你得到和放置时。

希望通配符可能开始变得更有意义。

【讨论】:

  • 如果“?”让你困惑,“?扩展对象”可能会让你更困惑。也许吧。
  • 试图提供“思考工具”,让人们对这个困难的主题进行推理。提供了有关扩展通配符的更多信息。
  • 感谢您提供更多信息。我还在消化它。 :)
  • HashMap&lt;String, ? extends Object&gt; 所以它只会阻止null 被添加到哈希图中?
【解决方案3】:

如果您还记得Collection&lt;Object&gt; 只是一个包含Object 类型对象的泛型集合,那就很容易理解了,而Collection&lt;?&gt; 是所有集合类型的超类型。

【讨论】:

    【解决方案4】:

    上述协方差的答案涵盖了大多数情况,但遗漏了一件事:

    “?”包括类层次结构中的“对象”。您可以说 String 是 Object 的一种类型,而 Object 是 ? 的一种类型。不是所有东西都匹配 Object,但所有东西都匹配?。

    int test1(List<?> l) {
      return l.size();
    }
    
    int test2(List<Object> l) {
      return l.size();
    }
    
    List<?> l1 = Lists.newArrayList();
    List<Object> l2 = Lists.newArrayList();
    test1(l1);  // compiles because any list will work
    test1(l2);  // compiles because any list will work
    test2(l1);  // fails because a ? might not be an Object
    test2(l2);  // compiled because Object matches Object
    

    【讨论】:

      【解决方案5】:

      您不能安全地将任何内容放入Map&lt;String, ?&gt;,因为您不知道这些值应该是什么类型。

      您可以将任何对象放入Map&lt;String, Object&gt;,因为已知值是Object

      【讨论】:

      • “你不能安全地将任何东西放入 Map” 假。你可以,这就是它的目的。
      • Ben 错了,您可以放入 > 类型的集合中的唯一值是 null,而您可以将任何内容放入 类型的集合中。
      • 来自我在回答中给出的链接:“因为我们不知道 c 的元素类型代表什么,所以我们不能向它添加对象。”。对于错误信息,我深表歉意。
      • 最大的问题是我不明白如果我们认为除了原语之外的所有东西都是对象,你怎么可能不能在 HashMap 中添加任何东西。还是用于原语?
      • @avalon 在其他地方,有对有界地图的引用。例如,它可能是Map&lt;String,Integer&gt;。只有Integer 对象应该作为值存储在地图中。但是由于您不知道值的类型(它是?),因此您不知道调用put(key, "x")put(key, 0) 或其他任何东西是否安全。
      【解决方案6】:

      hash1 声明为HashMap&lt;String, ?&gt; 表示变量hash1 可以包含任何HashMap,其键为String 和任何类型的值。

      HashMap<String, ?> map;
      map = new HashMap<String, Integer>();
      map = new HashMap<String, Object>();
      map = new HashMap<String, String>();
      

      以上所有内容都是有效的,因为变量 map 可以存储任何这些哈希映射。该变量不关心它所持有的哈希映射的 Value 类型是什么。

      使用通配符,但是,您可以将任何类型的对象放入地图中。事实上,使用上面的哈希映射,您不能使用map 变量将任何内容放入其中:

      map.put("A", new Integer(0));
      map.put("B", new Object());
      map.put("C", "Some String");
      

      上面所有的方法调用都会导致编译时错误,因为Java不知道map里面的HashMap的Value类型是什么。

      您仍然可以从哈希映射中获取值。尽管您“不知道值的类型”(因为您不知道变量中的哈希映射的类型),但您可以说一切都是Object 的子类,因此,无论您得到什么地图的类型将是 Object:

      HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
      
      myMap.put("ABC", 10);
      
      HashMap<String, ?> map = myMap;
      Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
      
      System.out.println(output);
      

      上面的代码块将打印 10 到控制台。


      所以,最后,当您不关心(即无关紧要)HashMap 的类型是什么时,请使用带有通配符的 HashMap,例如:

      public static void printHashMapSize(Map<?, ?> anyMap) {
          // This code doesn't care what type of HashMap is inside anyMap.
          System.out.println(anyMap.size());
      }
      

      否则,请指定您需要的类型:

      public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
          for (int i = 'A'; i <= 'Z'; i++)
              System.out.println(anyCharacterMap.get((char) i));
      }
      

      在上述方法中,我们需要知道 Map 的键是 Character,否则,我们将不知道使用什么类型从中获取值。但是,所有对象都有一个toString() 方法,因此映射可以具有任何类型的对象作为其值。我们仍然可以打印这些值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 2019-08-29
        • 1970-01-01
        • 2013-10-17
        • 2010-12-01
        相关资源
        最近更新 更多