简体   繁体   English

在泛型中使用参数类型时,列表的类型是什么?

[英]what's the type of list when the parameter's type is used in generics?

  public static void print(List<? extends Number> list) {
    for (Number n : list)
      System.out.print(n + " ");
    System.out.println();
  }

  public static void main(String[] args) {
    ArrayList<Integer> al = new ArrayList<>();
    print(al);
  }

I was wondering what's the type of list (the parameter).我想知道list的类型是什么(参数)。 is in the print()?是在打印()? is it Number ?Number吗? I think it is Number because if it is not the Number , how do we explain the Number n in the for loop?我认为它是Number因为如果它不是Number ,我们如何解释 for 循环中的Number n Do we usually use this in actual situations?我们通常在实际情况中使用它吗?

Your question uses ambiguous wording but asks about how we speak of things, so that's a problem.你的问题使用了模棱两可的措辞,但问的是我们如何说话,所以这是一个问题。 The type of the list param is List<? extends Number> list参数的类型是List<? extends Number> List<? extends Number> - it's right there in the text. List<? extends Number> - 它就在文本中。 It's obviously not Number , it is a list .它显然不是Number它是一个 list However, your question sounds like what you want to ask is: What is the component type of it.但是,您的问题听起来像是您要问的:它的组件类型是什么。

It is ? extends Number它是? extends Number ? extends Number . ? extends Number It's right there in the text.它就在文本中。 No, that's not the same as Number .不,这与Number You are trying to oversimplify generics, and as the word suggests, you can't do that.您试图过度简化泛型,正如这个词所暗示的那样,您不能这样做。 The concept (not in java, in physics / the world / mathematics) is not that simple.这个概念(不是在java中,在物理/世界/数学中)并不是那么简单。

What you're looking at, the word you want to look for, is variance .你在看什么,你想找的词,是variance

In java, generics have 4 different variances.在 Java 中,泛型有 4 种不同的变化。 These are all lists whose component type allows Number , but they are all just different:这些都是其组件类型允许Number列表,但它们都不同:

Variance name差异名称 What it looks like它看起来像什么
Invariant不变量 List<Number>
Covariant协变 List<? extends Number>
Contravariant逆变 List<? super Number>
Legacy/Raw传统/原始 List

The reason we need 4 is because there are 4 different things you may want to do with a 'list of numbers'.我们需要 4 的原因是因为您可能想要用“数字列表”做 4 种不同的事情。

Specifically, you can write to a list of numbers and read from it:具体来说,您可以写入数字列表并从中读取:

list.add(5.0); // 5.0 is a Double which is a kind of number

Number n = list.get(0);

There's writing and reading examples.有写作和阅读的例子。

If you write a method that does both, then only a List<Number> is allowed.如果您编写的方法同时执行这两项操作,则只允许使用List<Number> Clearly.清楚地。 Imagine that you wrote:想象一下,你写道:

void addFiveAndAHalf(List<Number> n) {
  n.add(5.5);
}

and it was legal in java to pass List<Integer> to this method (After all, Integer is a kind of number... passing 5 (an Integer ) to a method with sig foo(Number n) is legal, why wouldn't passing a List<Integer> be legal)?并且在 java 中将List<Integer>传递给这个方法是合法的(毕竟,Integer 是一种数字......将5 (一个Integer )传递给带有 sig foo(Number n)是合法的,为什么不t 传递List<Integer>是否合法)? - then you end up with a double in your list of ints. - 然后你最终在你的整数列表中得到一个双精度值。 whoops.哎呀。

So, where basic types are covariant (see below), generics just aren't.因此,在基本类型是协变的(见下文)的情况下,泛型不是。 They are invariant , only exactly the right type will do, a subtype is not good enough.它们是不变的,只有完全正确的类型才能做到,子类型还不够好。

But, then, you do get to read and write to it.但是,然后,您确实可以读取和写入它。

However, if you write a method that only reads (technically, only does things for which the precise type doesn't need to be known, ie does not call any method whose parameters include the generics, such as add or addAll ), then this rule goes away.但是,如果您编写一个只读取的方法(从技术上讲,只执行不需要知道精确类型的事情,即不调用任何参数包含泛型的方法,例如addaddAll ),那么这规则消失。 Now writing a method that expects a list of numbers is just fine with being handed a list of integers, if all it does is read.现在编写一个需要数字列表的方法,如果它只是读取一个整数列表,那么它就可以了。 All integers are numbers.所有整数都是数字。 But java does not know this , and is not going to analyse your code to try to figure out.但是java不知道这一点,也不会分析你的代码来试图弄清楚。 Even if it could do that, that means you can never change that method ever again in the future because it might change how that method's very signature is treated.即使它可以做到这一点,这也意味着您将来永远无法再更改该方法,因为它可能会改变该方法的签名的处理方式。 That's no good: The programmer needs to explicitly spell it out.那不好:程序员需要明确说明它。

And you can do that:你可以这样做:

void foo(List<? extends Number> n) {
   Number a = n.get(0); // legal
   n.add(5.5); // compiler error!
}

This method effectively says: It won't call any method on the list that needs the type (so, no add or addAll ), therefore if you want to hand me a list of some subtype of Number instead, that's just fine.该方法有效地表示:它不会调用需要该类型的列表上的任何方法(因此,没有addaddAll ),因此,如果您想给我一个 Number 的某个子类型的列表,那很好。

Contravariance is much more rare and shows up if you want to invoke methods that need the type as parameter, but you never have any need for the generics type in any return type.逆变更为罕见,如果您想调用需要该类型作为参数的方法,就会出现这种情况,但在任何返回类型中都不需要泛型类型。 For example, List's public E get(int idx) method, if you never call that, but you DO call add in your method, you want Contravariance: Any supertype is fine, but subtypes are not:例如,List 的public E get(int idx)方法,如果你从不调用它,但你确实在你的方法中调用了add ,你需要逆变:任何超类型都可以,但子类型不是:

void foo(List<? super Number> n) {
   n.add(5.5); // compiles
   Number a = n.get(0); // compiler error
}

After all, adding a Double to a List<Double> is fine.毕竟,将 Double 添加到List<Double>很好。 It's also fine to add it to a List<Number> or List<Object> .将它添加到List<Number>List<Object>也很好。

If you want to say it in words, the type of this parameter is "A list of covariant Number".如果要说的话,这个参数的类型是“协变数列表”。 But java coders would never say this when talking to each other, they'd just say "A list of extends Number" at best, or just 'A list of numbers' and the covariance is lost in the simplified non-technical chatter.但是 Java 编码人员在相互交谈时永远不会这样说,他们最多只会说“扩展数字列表”,或者只是“数字列表”,而协方差在简化的非技术闲聊中丢失了。

void foo(List<Number> n) {
  //

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM