<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>泛型 on 蛋蛋忧桑</title><link>https://dandansad.com/tags/%E6%B3%9B%E5%9E%8B/</link><description>Recent content in 泛型 on 蛋蛋忧桑</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Sun, 02 Mar 2025 02:27:33 +0800</lastBuildDate><atom:link href="https://dandansad.com/tags/%E6%B3%9B%E5%9E%8B/index.xml" rel="self" type="application/rss+xml"/><item><title>Java泛型-PECS原则</title><link>https://dandansad.com/2025/03/02/java%E6%B3%9B%E5%9E%8B-pecs%E5%8E%9F%E5%88%99/</link><pubDate>Sun, 02 Mar 2025 02:27:33 +0800</pubDate><guid>https://dandansad.com/2025/03/02/java%E6%B3%9B%E5%9E%8B-pecs%E5%8E%9F%E5%88%99/</guid><description>&lt;h2 id="什么是pecs原则"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%afpecs%e5%8e%9f%e5%88%99" class="header-anchor"&gt;&lt;/a&gt;什么是PECS原则？
&lt;/h2&gt;&lt;p&gt;PECS 是 “Producer Extends, Consumer Super” 的缩写。换句话说，如果参数化类型表示一个生产者（只读）就使用&amp;lt;? extends T&amp;gt;，如果它表示一个消费者（只写）就使用&amp;lt;? super T&amp;gt;。这个原则在使用泛型限定通配符时非常有用，可以帮助我们决定何时使用 &amp;lt;? extends T&amp;gt; 和 &amp;lt;? super T&amp;gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;? extends T&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;? super T&amp;gt;&lt;/code&gt;是Java泛型中的“通配符（Wildcards）”和“边界（Bounds）”的概念。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;? extends T&amp;gt;&lt;/code&gt;：是指 “上界通配符（Upper Bounds Wildcards）”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;? super T&amp;gt;&lt;/code&gt;：是指 “下界通配符（Lower Bounds Wildcards）”&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="为什么要有限定通配符"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e6%9c%89%e9%99%90%e5%ae%9a%e9%80%9a%e9%85%8d%e7%ac%a6" class="header-anchor"&gt;&lt;/a&gt;为什么要有限定通配符？
&lt;/h2&gt;&lt;p&gt;假设有这样一些类型定义。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//Lev 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Food&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//Lev 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fruit&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Food{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Meat&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Food{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//Lev 3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Apple&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Fruit{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Banana&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Fruit{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Pork&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Meat{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Beef&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Meat{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//Lev 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RedApple&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Apple{}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;GreenApple&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Apple{}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定义一个容器，Plate类。盘子里可以放一个泛型的“东西”：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Plate&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; T item;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Plate&lt;/span&gt;(T t){item&lt;span style="color:#f92672"&gt;=&lt;/span&gt;t;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;set&lt;/span&gt;(T t){item&lt;span style="color:#f92672"&gt;=&lt;/span&gt;t;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; T &lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(){&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; item;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在我定义一个“水果盘子”，逻辑上水果盘子当然可以装苹果。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Plate p&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Plate(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Apple());
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但实际上Java编译器不允许这个操作。会报错，“装苹果的盘子”无法转换成“装水果的盘子”。&lt;/p&gt;
&lt;p&gt;其实很好解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;苹果 IS-A 水果&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;装苹果的盘子 NOT-IS-A 装水果的盘子&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，就算容器里装的东西之间有继承关系，但容器之间是没有继承关系的。于是有了&lt;code&gt;&amp;lt;? extends T&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;? super T&amp;gt;。&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;上界通配符 &lt;code&gt;Plate&amp;lt;？ extends Fruit&amp;gt;&lt;/code&gt; 覆盖下图中蓝色的区域。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/03/image-3-1024x496.png"&gt;&lt;/p&gt;
&lt;p&gt;下界通配符 &lt;code&gt;Plate&amp;lt;？ super Fruit&amp;gt;&lt;/code&gt; 覆盖下图中红色的区域。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="../../images/2025/03/image-4-1024x526.png"&gt;&lt;/p&gt;
&lt;p&gt;接下来看一下PECS，再举个例子：&lt;/p&gt;
&lt;p&gt;下面是一个简单的Stack的API接口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Stack&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Stack&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;push&lt;/span&gt;(E e):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; E &lt;span style="color:#a6e22e"&gt;pop&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;boolean&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isEmpty&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//按顺序将一系列元素全部放入Stack中，你可能想到的实现方式如下：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pushAll&lt;/span&gt;(Iterable src){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(E e : src)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; push(e)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这时，有个&lt;code&gt;Stack&amp;lt;Number&amp;gt;&lt;/code&gt;，想要灵活的处理Integer，Long等Number的子类型的集合:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stack numberStack &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Stack();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Iterable integers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ....;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;numberStack.&lt;span style="color:#a6e22e"&gt;pushAll&lt;/span&gt;(integers);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此时代码编译无法通过，因为对于类型Number和Integer来说，虽然后者是Number的子类，但是对于任意Number集合（如&lt;code&gt;List&amp;lt;Number&amp;gt;&lt;/code&gt;）不是Integer集合（如&lt;code&gt;List&amp;lt;Integer&amp;gt;&lt;/code&gt;）的超类，因为泛型是不可变的。&lt;/p&gt;
&lt;p&gt;幸好java提供了一种叫有限通配符的参数化类型，pushAll参数替换为“E的某个子类型的Iterable接口”：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pushAll&lt;/span&gt;(Iterable src){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (E e: src)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; push(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样就可以正确编译了，这里的&amp;lt;? extends E&amp;gt;就是所谓的 producer-extends。这里的Iterable就是生产者，要使用&amp;lt;? extends E&amp;gt;。因为Iterable&amp;lt;? extends E&amp;gt;可以容纳任何E的子类。在执行操作时，可迭代对象的每个元素都可以当作是E来操作。&lt;/p&gt;
&lt;p&gt;与之对应的是：假设有一个方法popAll()方法，从Stack集合中弹出每个元素，添加到指定集合中去:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;popAll&lt;/span&gt;(Collection dst){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;isEmpty()){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dst.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(pop());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设有一个&lt;code&gt;Stack&amp;lt;Number&amp;gt;&lt;/code&gt;和&lt;code&gt;Collection&amp;lt;Object&amp;gt;&lt;/code&gt;对象：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Stack numberStack &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Stack();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Collection objects &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ...;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;numberStack.&lt;span style="color:#a6e22e"&gt;popAll&lt;/span&gt;(objects);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同样上面这段代码也无法通过，解决的办法就是使用Collection&amp;lt;? super E&amp;gt;。这里的objects是消费者，因为是添加元素到objects集合中去。使用Collection&amp;lt;? super E&amp;gt;后，无论objects是什么类型的集合，满足一点的是他是E的超类，所以不管这个参数化类型具体是什么类型都能将E装进objects集合中去。&lt;/p&gt;
&lt;h2 id="pecs原则的运用"&gt;&lt;a href="#pecs%e5%8e%9f%e5%88%99%e7%9a%84%e8%bf%90%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;PECS原则的运用
&lt;/h2&gt;&lt;h3 id="案例一"&gt;&lt;a href="#%e6%a1%88%e4%be%8b%e4%b8%80" class="header-anchor"&gt;&lt;/a&gt;案例一
&lt;/h3&gt;&lt;p&gt;btw，我们反过来呢？以下两种均不能通过编译。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pushAll&lt;/span&gt;(Iterable src){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (E e: src)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; push(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因为下界规定了元素的最小粒度的下限，实际上是放松了容器元素的类型控制。既然元素是E的基类，那往里存粒度比E小的都可以。但往外读取元素就费劲了，只有所有类的基类Object对象才能装下。但这样的话，元素的类型信息就全部丢失。Stack自然也无法存E的基类。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;popAll&lt;/span&gt;(Collection dst){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(&lt;span style="color:#f92672"&gt;!&lt;/span&gt;isEmpty()){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dst.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(pop());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;原因是编译器只知道容器内是Fruit或者它的派生类，但具体是什么类型不知道。编译器在?标上一个占位符：CAP#1，来表示捕获一个E或E的子类，具体是什么类不知道，代号CAP#1。然后无论是想往里插入A或者B或者C编译器都不知道能不能和这个CAP#1匹配，所以就都不允许。&lt;/p&gt;
&lt;h3 id="案例二"&gt;&lt;a href="#%e6%a1%88%e4%be%8b%e4%ba%8c" class="header-anchor"&gt;&lt;/a&gt;案例二
&lt;/h3&gt;&lt;p&gt;为了更好地解释这些概念，我们先定义一些类：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Fruit&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Apple&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Fruit {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Banana&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Fruit {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;List&amp;lt;? extends Fruit&amp;gt; 的理解&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;正如字面意思， 表示泛型的类型是 Fruit 或者 Fruit 的子类。也就是说，我们给 list 赋值时，泛型可以是 Fruit 或者 Fruit 的子类，比如 new ArrayList()、new ArrayList() 或者 new ArrayList()。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; List &lt;span style="color:#a6e22e"&gt;getExtendsList&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List list;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; list;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体来说，List 允许我们将 List、List 和 List 都赋值给它。然而，虽然我们可以从这个列表中获取元素（因为我们知道它们至少是 Fruit 类型），但我们不能向其中添加元素（除了 null），因为编译器无法确定我们实际的列表类型。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m1&lt;/span&gt;(List list) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Fruit fruit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; list.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(0); &lt;span style="color:#75715e"&gt;// 安全的，因为我们知道列表中至少是Fruit类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// list.add(new Apple()); // 错误，编译器不允许，因为list可能是new ArrayList()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;List&amp;lt;? super Fruit&amp;gt; 的理解&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;? super Fruit&amp;gt;&lt;/code&gt; 表示泛型的类型是 &lt;code&gt;Fruit&lt;/code&gt; 或者 &lt;code&gt;Fruit&lt;/code&gt; 的父类。也就是说，我们给 &lt;code&gt;list&lt;/code&gt; 赋值时，泛型可以是 &lt;code&gt;Fruit&lt;/code&gt; 或者 &lt;code&gt;Fruit&lt;/code&gt; 的父类，比如 &lt;code&gt;new ArrayList&amp;lt;Fruit&amp;gt;()&lt;/code&gt; 或者 &lt;code&gt;new ArrayList&amp;lt;Object&amp;gt;()&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; List &lt;span style="color:#a6e22e"&gt;getSuperList&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; List list;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; list;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;具体来说，&lt;code&gt;List&amp;lt;? super Fruit&amp;gt;&lt;/code&gt; 允许我们向列表中添加 &lt;code&gt;Fruit&lt;/code&gt; 或者 &lt;code&gt;Fruit&lt;/code&gt; 的父类，但我们不能安全地获取特定类型的元素，只能获取 &lt;code&gt;Object&lt;/code&gt; 类型的元素。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;m2&lt;/span&gt;(List list) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Apple()); &lt;span style="color:#75715e"&gt;// 可以，因为Apple是Fruit的子类&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Banana()); &lt;span style="color:#75715e"&gt;// 可以，因为Banana是Fruit的子类&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Object object &lt;span style="color:#f92672"&gt;=&lt;/span&gt; list.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(0); &lt;span style="color:#75715e"&gt;// 只能是Object类型，因为我们不知道具体的类型&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;List list：表示泛型的类型是 Fruit 或 Fruit 的子类，一般用于只获取元素。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;List list：表示泛型的类型是 Fruit 或 Fruit 的父类，一般用于只添加元素（获取出来的元素是 Object 类型，泛型意义不大）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;List list：明确的泛型，可以获取元素，也可以添加元素，是最常用的泛型。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="案例三"&gt;&lt;a href="#%e6%a1%88%e4%be%8b%e4%b8%89" class="header-anchor"&gt;&lt;/a&gt;案例三
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1. Producer Extends&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;: 当集合是生产者（提供数据）时，使用 &lt;code&gt;&amp;lt;? extends T&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;含义&lt;/strong&gt;: 集合中的元素是 &lt;code&gt;T&lt;/code&gt; 或其子类，只能从中读取数据，不能写入（除 &lt;code&gt;null&lt;/code&gt; 外）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;List numbers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Number num &lt;span style="color:#f92672"&gt;=&lt;/span&gt; numbers.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(0); &lt;span style="color:#75715e"&gt;// 可以读取&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// numbers.add(1); // 编译错误，不能写入&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Consumer Super&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;: 当集合是消费者（接受数据）时，使用 &lt;code&gt;&amp;lt;? super T&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;含义&lt;/strong&gt;: 集合中的元素是 &lt;code&gt;T&lt;/code&gt; 或其父类，只能写入 &lt;code&gt;T&lt;/code&gt; 或其子类的数据，读取时只能得到 &lt;code&gt;Object&lt;/code&gt; 类型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;示例&lt;/strong&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;List integers &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; ArrayList();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;integers.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(1); &lt;span style="color:#75715e"&gt;// 可以写入&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Integer i = integers.get(0); // 编译错误，读取时只能得到 Object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Object obj &lt;span style="color:#f92672"&gt;=&lt;/span&gt; integers.&lt;span style="color:#a6e22e"&gt;get&lt;/span&gt;(0); &lt;span style="color:#75715e"&gt;// 可以读取为 Object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="参考文章"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0" class="header-anchor"&gt;&lt;/a&gt;参考文章
&lt;/h2&gt;&lt;p&gt;&lt;a class="link" href="https://www.cnblogs.com/steve-jiang/p/12172273.html" target="_blank" rel="noopener"
 &gt;泛型-PECS原则 - iiiorz - 博客园&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://blog.csdn.net/weixin_41883161/article/details/137018351" target="_blank" rel="noopener"
 &gt;[简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)-CSDN博客&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>