使用通配符的Java集合

使用通配符的Java集合

Java Collections using wildcard

1
2
3
4
5
6
7
public static void main(String[] args) {

    List<? extends Object> mylist = new ArrayList<Object>();

    mylist.add("Java"); // compile error

}

上面的代码不允许您将元素添加到列表中,通配符只能用作方法中的签名,同样不能用于添加,而只能用于访问。
在这种情况下,以上目的是什么?


假设您有一个接口和两个类:

1
2
3
interface IResult {}
class AResult implements IResult {}
class BResult implements IResult {}

然后,您将得到返回结果列表的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
interface ITest<T extends IResult> {
  List< T > getResult();
}

class ATest implements ITest<AResult> {
  // look, overridden!
  List<AResult> getResult();
}

class BTest implements ITest<BResult> {
  // overridden again!
  List<BResult> getResult();
}

当您需要"协变量返回",但是您返回集合而不是自己的对象时,这是一个很好的解决方案。最大的好处是,当独立于ITest接口使用ATest和BTest时,不必强制转换对象。但是,使用ITest接口时,不能将任何东西添加到返回的列表中-无法确定列表真正包含的对象类型!如果允许,则可以将BResult添加到List (作为List <?扩展T>返回),这没有任何意义。

因此,您必须记住以下内容:List <? X>扩展定义了一个可以轻松覆盖的列表,但它是只读的。


约书亚·布洛赫(Joshua Bloch)在他的著作《有效的Java》(第二版)中解释了他所谓的使用泛型的生产者/消费者原则。 Josh的解释应该告诉您为什么您的示例不起作用(编译)...

第5章(泛型)可在此处免费获得:http://java.sun.com/docs/books/effective/generics.pdf

有关此书(和作者)的更多信息,请访问:http://java.sun.com/docs/books/effective/


有界通配符类型的要点是它们在方法签名中的使用,以增加API的灵活性。例如,如果实现了通用Stack,则可以提供一种将多个元素压入堆栈的方法,如下所示:

1
2
3
4
5
public void pushAll(Iterable<? extends E> elements) {
    for(E element : elements){
       push(e);
    }
}

与没有通配符的pushAll(Iterable elements)签名相比,它的优点在于它允许将E的子类型的集合传递给方法-通常不被允许,因为Iterable在某种程度上与直觉相反,不是Iterable的子类。


对于使用通配符的Java泛型,假设只打算从通配符中读取,则可以使用上述声明。

不允许对其进行添加/写入,因为必须在编译时剥离所有泛型类型,并且在编译时,编译器没有办法知道List只是字符串,(它可以是任何对象,包括字符串! )

但是,您可以从中读取内容,因为它们将至少是对象。在Java集合中不允许混合使用不同的类型以保持内容的清洁和可理解,这有助于确保这一点。


Java泛型:集合中的通配符

  • 延伸
  • 今天,我将向您解释通配符的作用。理解这个概念有点困难

    现在,假设您有一个抽象类,并且您有一个名为paintObject()的抽象方法。

    1
    Now you want to use different type of collection in every child class.

    这是AbstractMain方法。

    在这里,我们针对此Abstract Main方法采取的步骤

    1.我们创建了抽象类

    2.在参数中,我们定义了T(您可以使用任何字符)
    -在这种情况下,无论哪个类实现了此方法,它都可以使用任何类型的类。
    例如类可以实现类似的方法
    public void paintObject(ArrayList对象)或public void paintObject(HashSet对象)

    3.并且我们还使用了E扩展MainColorTO
    -在这种情况下E扩展MainColorTo
    -显然这意味着您要使用的任何类都必须是MainColorTo的子类

    4.我们定义了一个抽象方法,称为paintObject(T object,E objectTO)
    -现在无论哪种类都是实现方法,该方法都可以在该方法必须使用MainColorTO类型的第一个参数和第二个参数上使用任何类

    1
    2
    3
    public abstract class AbstractMain<T,E extends MainColorTO> {
          public abstract void paintObject(T Object,E TO);  
    }

    现在我们将扩展到抽象类之上,并在其下实现方法
    例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class MainColorTO {  
         public void paintColor(){
               System.out.println("Paint Color........");
         }
      }

    public class RedTO extends MainColorTO {
       @Override
       public void paintColor() {
       System.out.println("RedTO......");
     }
    }
    public class WhiteTO extends MainColorTO {
       @Override
       public void paintColor() {
         System.out.println("White TO......");
       }
     }

    现在我们举两个例子。

    1.PaintHome.java

    1
    2
    3
    4
    5
    6
    7
    public class PaintHome extends AbstractMain<ArrayList, RedTO> {
        @Override
        public void paintObject(ArrayList arrayList,RedTO red) {
            System.out.println(arrayList);

        }
     }

    现在在上面的PaintHome.java中,您可以检查我们是否在第一个参数中使用了ArrayList(因为可以使用任何类),在第二个参数中我们使用了RedTO(正在扩展MainColorTO)

    2,PaintCar.java

    1
    2
    3
    4
    5
    6
    7
    public class PaintCar extends AbstractMain<HashSet, WhiteTO>{
        @Override
        public void paintObject(HashSet Object,WhiteTO white) {
            System.out.println(Object);

        }
     }

    现在在上面的PaintCar.java中,您可以检查我们是否在第一个参数中使用了HashSet(因为我们可以使用任何类),在第二个参数中我们使用了WhiteTO(正在扩展MainColorTO)

    要记住的Ponint
    您不能在课程级别使用super关键字,而只能在课程级别使用extends关键字

    1
    2
    3
    4
    5
    public abstract class AbstractMain<P,E super MainColorTO> {

        public abstract void paintObject(P Object,E TO);

    }

    上面的代码将给您编译器错误。


    List< ? >相同的List<? extends Object>满足了归纳所有类型ListListList等的目的(因此,所有具有正确类型的类型都可以代替?) 。所有这些类型的值都可以分配给类型List< ? >的变量(这与List有所不同!)。

    通常,您不能将字符串添加到此类列表。但是,您可以从列表中读取Object,也可以添加null。您还可以计算列表的长度,等等。这些是可以保证对每种类型都适用的操作。

    有关通配符的良好介绍,请参见论文"将通配符添加到Java编程语言"。这是一篇学术论文,但仍然很容易阅读。


    这有效:

    1
    2
    List<? super Object> mylist = new ArrayList<Object>();
    mylist.add("Java"); // no compile error

    从O'Reilly的Java泛型:

    The Get and Put Principle: use an extends wildcard when you only get values our of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard you both get and put.


    推荐阅读

      程式编辑快捷键|代码编辑快捷键

      程式编辑快捷键|代码编辑快捷键,,1. 代码编辑快捷键Option Explicit ‘强制对模块内所有变量进行声明(2) Option Base 1 ‘指定数组的第一

      vivo网络设置代码|vivo功能代码

      vivo网络设置代码|vivo功能代码,,1. vivo功能代码代码是*#558#1.首先在手机桌面里找到电话。2.在拨号键盘里输入*#558#3.这时候就进入了手

      确定java按钮响应事件的代码

      确定java按钮响应事件的代码,,* 阅读本文可以结合最后在java登录窗口界面下面是一个链接。 是定义的容器。 容器(CP =得到内容面板); / /

      excel窗口快捷键|excel代码窗口快捷键

      excel窗口快捷键|excel代码窗口快捷键,,excel代码窗口快捷键首先是新建并打开Excel表格,萊垍頭條按快捷键“Ctrl”+“F10”,可把窗口切换,再

      乐视股票代码

      乐视股票代码,股票,股票代码,乐视股票代码股票代码其实很容易理解,每一串代码代表的就是一只股票,用“平安银行”来举例说明吧,它的代码就是0

      asp中文图片验证码的实现代码

      asp中文图片验证码的实现代码,,这个代码是在别人的增加对汉字的基础功能,谢谢你,谢谢轻烟。 以前的图片验证代码很容易破解,所以在目前的基础

      计算机主板故障调试代码

      计算机主板故障调试代码,,核心提示:故障代码含义查找表的调试代码查找表来读:(注1),特殊代码00和FF等起始码有三种情况:一系列其他代码:00或FF,好