高级泛型:通配符

Java中的泛型类由sreekandank介绍了泛型的基础知识。然而,除了在那篇文章中解释之外,还有更多要学习的东西。在这里,我将讨论Java泛型的一个称为通配符的特定特性。

  1. 基本通配符
    泛型可能非常有用,但有时它们可​​能会有所限制。 例如,假设您有一个具有List 对象的通用类Wildcard 。 到目前为止,这没有问题。
    public class Wildcards { 
        private List list;
        public void setList(List list) {
            this.list = list;
        }
    }
    但是,假设我们现在要编写一个函数,该函数会将内部列表的长度与另一个当然也是通用的List进行比较。 这应该在Wildcard 类内发生,并且不应该依赖于第二个List具有与第一个相同的通用类型。第一个尝试可能是使Wildcard类更通用: Wildcard 。 现在我们可以编写函数了。
     public class Wildcards { 
        private List list;
        public void setList(List list) {
            this.list = list;
        }
        public boolean hasMoreElementsThan(List otherList) {
            return list.size() > otherList.size();
        }
    }
    那应该工作。 让我们测试一下:
    public static void main(String[] args) {
        // Create a new ArrayList of Strings and fill it with "Hello", "World" and "!"
        ArrayList list1 = new ArrayList();
        list1.add("Hello");
        list1.add("World");
        list1.add("!");
        // Create a new Wildcard object and set list1 as the internal list 
        Wildcards w1 = new Wildcards();
        w1.setList(list1);
        // Create a new LinkedList of Integers and fill it with the Numbers 0 through 9
        ArrayList list2 = new ArrayList();
        for(int i=0; i<10; i++) {
            list2.add(new Integer(i));
        }
        // Use the hasMoreElementsThan function
        if(w1.hasMoreElementsThan(list2)) {
            System.out.println("The internal list is bigger than the one it was compared to");
        } else {
            System.out.println("The internal list is not bigger than the one it was compared to");
        }
    }
    很好,应该可以。 但是有什么问题吗? 嗯,是。 假设我们还要将内部列表与第三个列表进行比较。 我们要么必须为要与列表进行比较的每种类型创建一个新的通配符实例,要么每次要添加新的比较时都要使类型的列表更长。 那不是很有效。 另一个想法可能是将第二个List转换为更通用的类型,例如LinkedList 。 但是,尝试该操作将导致以下错误消息:
     Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
        Cannot cast from LinkedList to LinkedList 为什么这样? 好吧,如果允许这种类型的转换,则还可以执行以下操作:
     
    ((LinkedList) list2).add(42); 希望您能看到问题。
    无论如何,这就是为什么在Java中存在通配符的概念。 而不是添加其他通用类型进行比较,我们告诉hasMoreElementsThan函数“我并不在乎,我要比较的是哪种列表。” 这是这样的:
    public boolean hasMoreElementsThan(List otherList) {
        return list.size() > otherList.size();
    }
    问号意味着我们不在乎类型。 它与将其强制转换为List 的方式不同 ,因为在这种情况下,我们没有任何关于List中可能存在哪种Object的信息。 通过调用我们没有关于泛型类型的任何信息。

    注意:仍然可以做类似的事情
    if(otherList.get(0) instanceof String) { … }
  2. 上限通配符
    尽管这可能有用,但有时我们希望对可以将哪些类型的参数传递给函数进行更多控制。 举例来说,我们想在Wildcards类中创建另一个函数,该函数将检查数字列表中的数字之一是否接近我们内部列表的大小。 可能看起来像这样:
    public boolean closeToSize(List otherList) {
        for(Number item : otherList) {
            if(item.intValue() == list.size())
                return true;
        }
        return false;
    }
    但是,等等-仅当我们的List实际上是List 时才有效,但如果它是List List 不起作用。 该怎么办?
    幸运的是,存在限制通配符的方法。 上限通配符是提供此功能的两种方法之一。 基本上,我们说“允许任何扩展Number的通用对象” 。 幸运的是,编写此代码非常简单:
     public boolean closeToSize(List otherList) {
        // Same as before
    }
    注意: extends X语法不仅适用于通配符,如果要访问通用类型,还可以使用类似
     public  boolean closeToSize2(List otherList) {
        for(int i=0; i
    但是,仅当实际需要该类型时才应使用此选项。
  3. 下限通配符
    如果存在上限通配符,则直觉告诉我们也可能存在下限通配符。 确实,它们存在! 需要它们的原因被称为PECS原理: 生产者扩展,消费者超级 。 但是,这是什么意思?
    在上限示例中,List生成了我们可以用来处理的项目。 因此,我们需要一些有关内容可能是哪种对象的信息。 但是,如果我们要创建一个新列表并让此新列表使用对象(=将对象添加到列表中),则需要知道可以放入其中的内容。 一个例子:
     public void unite(List listA, List listB) {
        for(Form item : listA) {
            listB.add(item);
        }
    }
     abstract class Form {
        protected double area; 
        public double getArea() {
            return area;
        }
    } 
    class Ellipse extends Form {
        public Ellipse(int rx, int ry) {
            super();
            area = Math.PI * rx * ry;
        }
    } 
    class Circle extends Ellipse {
        public Circle(int r) {
            super(r,r);
        }
    } 
    class Rectangle extends Form {
        public Rectangle(int x, int y) {
            area = x * y;
        }
    } 
    class Square extends Rectangle {
        public Square(int x) {
            super(x,x);
        }
    }
    如果我们尝试对其进行编译,并使用w1.unite(list4,list5) (列表分别为ArrayList 和ArrayList 类型)进行调用, 则会抛出以下RuntimeException:
     Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
        The method add(capture#4-of ? extends Form) in the type List is not applicable for the arguments (Form) 
    这样做的原因是程序无法知道这两个通用类型是否兼容。 毕竟,据我们所知,我们可能正在尝试将Squares添加到Circles列表中! 因此,我们也可以将通配符限制在另一个方向:
     public void unite(List listA, List listB) {
        for(Circle item : listA) {
            listB.add(item);
        }
    }
    这意味着listB的项目可以是Ellipse继承链中的任何项目; 当然,这只是形式 (抽象的)和椭圆本身。 但是,由于我们不是从listB中读取内容,而是仅对其进行写操作,因此Circles可以解释为Ellipses,这将起作用。
  4. 因此,这已经详细解释了Java泛型领域中的通配符。 完整的代码可以在下面下载。 因此,现在是时候利用这些新知识来做一些事情了!
    附加的文件
    文件类型:zip Wildcards.java.zip (1.8 KB,60视图)

    From: https://bytes.com/topic/java/insights/949458-advanced-generics-wildcards

    你可能感兴趣的:(高级泛型:通配符)