谁能用java中的有界通配符解释这段代码?

Big*_*gIO 0 java lower-bound

我知道我的问题可能看起来像重复的问题,但我已经阅读了许多类似的问题和答案,但我对下限 (? super) 特别感到困惑。请考虑这段代码。

 import java.util.*;
   public class Main {
  // just adds numbers to the List nums
   static void myadd(List<? super Number> nums){
    
    nums.add(1);
    nums.add(1.0f);
    nums.add(1.1);
    nums.add(3l);
  }
  public static void main(String[] args) {
      
      List empty= new ArrayList<>();
      myadd(empty);
      System.out.println(empty);
  }
}
Run Code Online (Sandbox Code Playgroud)

是执行它的链接。

根据甲骨文文档

下限通配符将未知类型限制为特定类型或该类型的超类型

Number类是 Double、Float、Integer、Long 和 Short 类的超类,那么为什么这个方法执行得很好呢? 此方法 (myadd) 应该仅适用于 Number 类或其超类型的直接实例的对象,而不适用于其子类。

我已阅读以下答案,但我的疑问仍不清楚。

  1. 了解 的上限和下限 ? 在 Java 泛型中
  2. Java 下限通配符

如果有人能解释我将不胜感激。

eri*_*son 6

List<? super Number>意味着只有List<Number>(或 的某个超类Number,即Object)可以分配给该变量(nums在您的示例中)。

\n

因为任何LongDouble等都是Number实例,因此它可以包含在List<Number>\xe2\x80\x94 中,或者就此而言,包含在List<Object>. 但是,如果要将列表中的元素分配给另一个变量,则该变量的类型必须为Object,因为列表的实际类型可能是List<Object>

\n

List<? extends Number>意味着List<Number>List<Long>List<Double>的任何其他子类Number可以分配给该变量。

\n

因为这样的列表只允许包含Number\xe2\x80\x94(Long如或Double\xe2\x80\x94)的某个子类,但特定的子类是未知的,所以不能安全地将任何内容添加到列表中。但是,它的任何元素都可以分配给 类型的变量Number

\n

考虑以下示例:

\n
List<Object> objs = new ArrayList<>();\nList<? super Number> nums = objs;\n// objs is `List<Object>` so clearly this is fine:\nobjs.add(Integer.valueOf(1));\nobjs.add("Hello, World!");\n// We\'ve forgotten that nums is actually the same `List<Object>`, so this won\'t compile:\nnums.add("Nice to meet you!"); \n// But we remember that nums is at least a `Number` so this is okay:\nnums.add(Double.valueOf(1.0));\n// This won\'t compile, because `nums` could/does have a `String` in it:\nNumber num = nums.get(0);\n// This is okay; nums could hold any superclass of `Number`\nObject obj = nums.get(0);\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n

那么它们之间有什么区别,因为最终我能够在 extends 和 super 中添加 Number 的子类?

\n
\n

不,这是错误的。正如我所说,“因为这样的列表只允许包含Number\xe2\x80\x94LongDouble\xe2\x80\x94 之类的某个子类,但特定子类未知,因此无法安全地将任何内容添加到列表中。” 您无法安全地将子类Number(或任何内容)添加到List<? extends Number>. 并不?意味着“任何子类”;它的意思是“未知子类”。这是一些Number你不知道的子类的列表。

\n
List<Integer> ints = new ArrayList<>();\nList<? extends Number> alias = ints;\n// This won\'t compile, because you\'d be polluting `ints` with a float.\nalias.add(Float.valueOf(1F));\nList<Number> anyNum = new ArrayList<>();\n// These adds are fine, anyNum can contain any subclass of Number\nanyNum.add(Integer.valueOf(1));\nanyNum.add(Float.valueOf(1F));\n// This is fine...\nalias = anyNum;\n// ...but these will fail, because we "forgot" that anyNum points to List<Number>\nalias.add(Integer.valueOf(2));\nalias.add(Float.value(2F));\n
Run Code Online (Sandbox Code Playgroud)\n
\n

请证明您在答案的第二段中所说的话“因为任何LongDouble等是一个Number实例,它可以包含在List<Number>\xe2\x80\x94 中,或者就此而言,一个List<Object>”我不同意因为LongDouble所有这些包装类都不是Number类的实例。相反,它们是类的子类的实例Number

\n
\n

我在那里使用了一些行话,“is-a”,你可能不熟悉。当一个类是另一个类的子类时,它应该完全可以替代超类。ALong或 aDouble可以执行 a 可以执行的任何操作Number;我们说Double“a is-a” Number。像这样的赋值Number x = Integer.valueOf(1)完全没问题,因为Integeris-a Number。当然,nums.add(x)完全没问题,因为 的类型xNumber. Child从类型到类型的(扩大​​)转换会Parent成功吗?然后Child是-a Parent

\n
\n

不过,我同意这一行“如果要将列表中的元素分配给另一个变量,则该变量的类型必须为Object,因为列表的实际类型可能是List<Object>。” 因为当我尝试将列表中的值分配给 Float 变量时,我必须将其类型转换为 float 。这引发了更多问题,因为如果 List 只能接受 Object 那么为什么它在 myadd() 方法中接受 Integer 、 FLoat .. ? ??

\n
\n

类型变量List<?>意味着列表具有某种类型,但我们不知道它是什么。没有任何东西可以安全地添加到它,如果我们从中取出一个元素,我们所知道的关于该类型的就是它是一个Object或任何子类,因此我们只能安全地将它分配给Object. 不要将其视为List<?>“任何类型的列表”,而应将其视为“未知类型的列表”。

\n

类型变量List<Object>包含任何“is-a” Object。由于任何类型最终都会扩展Object,因此任何类型都可以添加到此列表中。

\n

AList<Number>包含任何“is-a” NumberLongDouble等与 具有“is-a”(子类型)关系Number,因此可以将它们添加到列表中。我可以定义自己的自定义Number类型并将其添加到列表中。

\n

类型变量List<? extends Number>意味着列表具有某种类型,但我们只知道该类型是Number或一个子类。我们仍然无法安全地向其中添加任何内容,因为我们不知道它是否是List<Long>List<Double>或您拥有的其他内容的别名。但是,如果我们从中取出一个元素,我们可以将其分配给Number而不是Object,因为我们至少知道它是 -a Number

\n

类型变量List<? super Number>意味着列表具有某种类型,但我们只知道该类型是Number或超类。我们可以添加任何“is-a”的东西Number(包括子类)。但是,如果我们从中取出一个元素,我们不知道它是一个Number; 别名可以指向 a List<Object>,因此它可能包含任何类的对象。我们只能安全地将其元素分配给类型的变量Object

\n

实例化泛型类型时不能使用通配符。也就是说,你不能写new ArrayList<? extends Number>. 通配符仅像别名一样使用。不带通配符的值仅在带通配符的类型下使用别名,无论是在对变量赋值、作为参数传递还是作为方法结果返回期间。

\n

我一直使用“安全”这个词,因为 Java 泛型是关于类型安全的。如果您不抑制或忽略任何类型安全警告,则在运行时不会出现任何类转换异常,除非代码中存在相应的显式转换。如果抑制或忽略类型安全警告,则可能会在源代码中不可见强制转换的位置遇到类强制转换异常。编译器会为您插入这些隐式转换以支持泛型类型。

\n

回到你原来的例子,让我重写一下,问你的问题出在哪里。

\n
static void myadd(List<? super Number> nums) {\n    /* Create Number objects */\n    Number anInteger = 1;\n    Number aFloat = 1F;\n    Number aDouble = 1D;\n    Number aLong = 1L;\n    /* Add Number objects to list */\n    nums.add(anInteger);\n    nums.add(aFloat);\n    nums.add(aDouble);\n    nums.add(aLong);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

你不明白 a IntegerFloatDoubleLong可以赋值给 a 吗Number?或者您不明白 aNumber可以添加到 a 上吗List<? super Number>

\n

  • 出色的!我认为你渴望真正了解事物是如何运作的,这很好。太多的开发人员缺乏这种兴趣。 (2认同)