精华内容
下载资源
问答
  • 使用泛型的过程中,经常出现一种很别扭的情况 比如我们有Fruit类,和它的派生类Apple class Fruit {} class Apple extends Fruit {} 然后有一个最简单的容器:Plate类 盘子里可以放一个泛型的“东西” 我们可以对...

    1 为什么要用通配符和边界?

    使用泛型的过程中,经常出现一种很别扭的情况 比如我们有Fruit类,和它的派生类Apple

    class Fruit {}
    class Apple extends Fruit {}

    然后有一个最简单的容器:Plate类 盘子里可以放一个泛型的“东西” 我们可以对这个东西做最简单的“”和“”的动作:set( )get( )方法

    class Plate<T>{
        private T item;
        public Plate(T t){item=t;}
        public void set(T t){item=t;}
        public T get(){return item;}
    }

    现定义一个“水果盘”,逻辑上水果盘当然可以装苹果

    Plate<Fruit> p = new Plate<Apple>(new Apple());

    但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。

    error: incompatible types: Plate<Apple> cannot be converted to Plate<Fruit>

    实际上,编译器认定的逻辑是这样的:

    • 苹果 IS-A 水果
    • 装苹果的盘子 NOT-IS-A 装水果的盘子

    所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系 所以我们不可以把Plate<Apple>的引用传递给Plate<Fruit>

    为了让泛型用起来更舒服,Sun的大师们就想出了<? extends T>和<? super T>的办法,来让”水果盘子“和”苹果盘子“之间发生正当关系

    2 上界

    下面就是上界通配符(Upper Bounds Wildcards)

    Plate<? extends Fruit>

    一个能放水果以及一切是水果派生类的盘子 再直白点就是:啥水果都能放的盘子 这和我们人类的逻辑就比较接近了 Plate<? extends Fruit>和Plate<Apple>最大的区别就是:Plate<? extends Fruit>是Plate<Fruit>及Plate<Apple>的基类 直接的好处就是,我们可以用“苹果盘”给“水果盘”赋值了

    Plate<? extends Fruit> p = new Plate<Apple>(new Apple());

    再扩展一下,食物分成水果和肉类,水果有苹果和香蕉,肉类有猪肉和牛肉,苹果还有两种青苹果和红苹果

    //Lev 1
    class Food{}
    
    //Lev 2
    class Fruit extends Food{}
    class Meat extends Food{}
    
    //Lev 3
    class Apple extends Fruit{}
    class Banana extends Fruit{}
    class Pork extends Meat{}
    class Beef extends Meat{}
    
    //Lev 4
    class RedApple extends Apple{}
    class GreenApple extends Apple{}

    在这个体系中,上界通配符Plate<? extends Fruit>覆盖下图中蓝色的区域

    3 下界

    相对应的下界通配符(Lower Bounds Wildcards)

    Plate<? super Fruit>

    表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子 Plate<? super Fruit>是Plate<Fruit>的基类,但不是Plate<Apple>的基类 对应刚才那个例子,Plate<? super Fruit>覆盖下图中红色的区域。

    4 上下界通配符的副作用

    边界让Java不同泛型之间的转换更容易了。但不要忘记,这样的转换也有一定的副作用。那就是容器的部分功能可能失效。

    还是以刚才的Plate为例。我们可以对盘子做两件事,往盘子里set( )新东西,以及从盘子里get( )东西

    class Plate<T>{
        private T item;
        public Plate(T t){item=t;}
        public void set(T t){item=t;}
        public T get(){return item;}
    }

    4.1 上界<? extends T>不能往里存,只能往外取

    <? extends Fruit>会使往盘子里放东西的set( )方法失效 但取东西get( )方法还有效

    比如下面例子里两个set()方法,插入Apple和Fruit都报错

    Plate<? extends Fruit> p = new Plate<Apple>(new Apple());
    
    //不能存入任何元素
    p.set(new Fruit());    //Error
    p.set(new Apple());    //Error
    
    //读取出来的东西只能存放在Fruit或它的基类里。
    Fruit newFruit1=p.get();
    Object newFruit2=p.get();
    Apple newFruit3=p.get();    //Error

    编译器只知道容器内是Fruit或者它的派生类,但具体是什么类型不知道 可能是Fruit?可能是Apple?也可能是Banana,RedApple,GreenApple?编译器在看到后面用Plate<Apple>赋值以后,盘子里没有被标上有“苹果”。而是标上一个占位符:capture#1,来表示捕获一个Fruit或Fruit的子类,具体是什么类不知道,代号capture#1 然后无论是想往里插入Apple或者Meat或者Fruit编译器都不知道能不能和这个capture#1匹配,所以就都不允许

    所以通配符<?>和类型参数<T>的区别就在于,对编译器来说所有的T都代表同一种类型 比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer...

    public <T> List<T> fill(T... t);

    但通配符<?>没有这种约束,Plate<?>单纯的就表示:盘子里放了一个东西,是什么我不知道

    4.2 下界<? super T>不影响往里存,但往外取只能放在Object对象里

    使用下界<? super Fruit>会使从盘子里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

    Plate<? super Fruit> p=new Plate<Fruit>(new Fruit());
    
    //存入元素正常
    p.set(new Fruit());
    p.set(new Apple());
    
    //读取出来的东西只能存放在Object类里。
    Apple newFruit3=p.get();    //Error
    Fruit newFruit1=p.get();    //Error
    Object newFruit2=p.get();

    因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制 既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

    5 PECS原则

    最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了

    1. 频繁往外读取内容的,适合用上界Extends
    2. 经常往里插入的,适合用下界Super

    from: https://cloud.tencent.com/developer/article/1353548 

    展开全文
  • Java 泛型super和extend

    千次阅读 2018-01-04 17:04:24
    假设:Men extends Person 但是不能 List&lt;Person&gt; list = new List&lt;Men&gt;();... 因为: Men is-a Person 存在继承关系 ... 这让泛型用起来很不舒服,为解决这个问题,所以: ? 通配符...
    • 假设:Men extends Person
    • 但是不能 List<Person> list = new List<Men>(); 会报错!
    • 因为: Men is-a Person  存在继承关系
    • 但是:List<Men> is-not-a List<Person>   不存在继承关系
    • 这让泛型用起来很不舒服,为解决这个问题,所以:
    • ? 通配符类型
    • <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
    • <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object
    • 现在:List<?extends Person> list = new List<Men>(); 就合法了。

    请记住PECS原则:生产者(Producer)使用extends,消费者(Consumer)使用super。

    生产者使用extends

    List<? extendsPerson>表示该list集合中存放的都是Person的子类型(包括Person自身),由于Person的子类型可能有很多,但是我们存放元素时实际上只能存放其中的一种子类型(这是为了泛型安全,因为其会在编译期间生成桥接方法<Bridge Methods>该方法中会出现强制转换,若出现多种子类型,则会强制转换失败)。因此你不能往该列表中添加任何元素,(因为你不知道里面到底存储的是什么类型)。List<? extends Person>不能添加元素,但是由于其中的元素都有一个共性--有共同的父类,因此我们在获取元素时可以将他们统一强制转换为Person类型,相当于一个只读List。

    消费者使用super

    对于List<? super Men>其list中存放的都是Men的父类型元素(包括Men),我们在向其添加元素时,只能向其添加Men的子类型元素(包括Men类型),这样在编译期间将其强制转换为Men类型时是类型安全的,因此可以添加元素但是由于该集合中的元素都是Men的父类型(包括Men),其中的元素类型众多,在获取元素时我们无法判断是哪一种类型,故设计成不能获取元素,相当于一个只写List。

    展开全文
  • Java 泛型Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。...

    Java 泛型

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

    泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

    适用于多种数据类型执行相同的代码

    泛型中的类型在使用时指定

    泛型归根到底就是“模版”

    优点:使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。

    泛型主要使用在集合中。

    泛型的使用

    泛型有三种常用的使用方式:泛型类,泛型接口和泛型方法。

    泛型类

    一个泛型类(generic class)就是具有一个或多个类型变量的类。

    语法:

    class 类名称 {

    private 泛型标识 /*(成员变量类型)*/ var;

    .....

    }

    }

    例子:

    //此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

    //在实例化泛型类时,必须指定T的具体类型

    public class GenericTest {

    //value这个成员变量的类型为T,T的类型由外部指定

    private T value;

    //泛型构造方法形参value的类型也为T,T的类型由外部指定

    public GenericTest(T value) {

    this.value = value;

    }

    //泛型方法getValue的返回值类型为T,T的类型由外部指定

    public T getValue() {

    return value;

    }

    public void setValue(T value) {

    this.value = value;

    }

    }

    注意:

    泛型的类型参数只能是类类型,不能是简单类型。

    传入的实参类型需与泛型的类型参数类型相同。

    在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。

    不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。

    if(ex_num instanceof Generic){ }

    泛型接口

    泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生成器中,生成器相当于对象工厂,是一种专门用来创建对象的类。

    例子:

    //定义一个泛型接口

    public interface Generator {

    public T next();

    }

    继承接口:

    当实现泛型接口的类,传入泛型实参时:

    public class FruitGenerator implements Generator {

    @Override

    public String next() {

    return null;

    }

    }

    当实现泛型接口的类,未传入泛型实参时:

    public class FruitGenerator implements Generator {

    private T next;

    public FruitGenerator(T next) {

    this.next = next;

    }

    @Override

    public T next() {

    return next;

    }

    public static void main(String[] args){

    FruitGenerator fruit = new FruitGenerator<>("Fruit");

    System.out.println(fruit.next);

    }

    }

    泛型方法

    可以使用泛型来表示方法。

    例子:

    public void say(T x){

    System.out.println(x.getClass().getName());

    }

    泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:

    无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。所以如果static方法要使用泛型能力,就必须使其成为泛型方法。

    泛型通配符

    类型通配符一般是使用?代替具体的类型实参,注意了,此处’ ?’是类型实参,而不是类型形参 。

    可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

    例子:

    public static void generic(List> list){

    for (Object o : list) {

    }

    }

    上界通配符: extends ClassType>该通配符为 ClassType的所有子类型。它表示的是任何类型都是ClassType类型的子类。

    例子:

    public static void generic(List extends String > list){

    for (Object o : list) {

    }

    }

    下界通配符: super ClassType>该通配符为 ClassType的所有超类型。它表示的是任何类型的父类都是 ClassType。

    例子:

    public static void generic(List super String > list){

    for (Object o : list) {

    }

    }

    标签:Java,value,next,类型,泛型,实参,public

    来源: https://www.cnblogs.com/yt0617/p/14443502.html

    展开全文
  • 主要介绍了Java泛型extends及super区别实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • JAVA 泛型通配符super

    2020-09-18 10:48:04
    super Number这个类型限定为Number的所有超类。 语句1:List<? super Integer> 无法确定sList中存放的对象的具体类型,因此sList.get获取的值存在不确定性,子类对象的引用无法赋值给兄弟类的引用,父类...

    超类型限定和子类型限定相反,可以给方法提供参数,但是不能使用返回值。? super Number这个类型限定为Number的所有超类。

    语句1:List<? super Integer> 无法确定sList中存放的对象的具体类型,因此sList.get获取的值存在不确定性,子类对象的引用无法赋值给兄弟类的引用,父类对象的引用无法赋值给子类的引用,因此语句错误。

    语句2:List<? super Integer> 无法确定sList中存放的对象的具体类型,因此sList.get获取的值存在不确定性,子类对象的引用无法赋值给兄弟类的引用,父类对象的引用无法赋值给子类的引用,因此语句错误。

    语句3:子类对象的引用可以赋值给父类对象的引用,因此语句正确。

    下界<? super T>不影响往里存,但往外取只能放在Object对象里

    使用下界<? super T>会使从这里取东西的get( )方法部分失效,只能存放到Object对象里。set( )方法正常。

    因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Fruit的基类,那往里存粒度比Fruit小的都可以。但往外读取元素就费劲了,只有所有类的基类Object对象才能装下。但这样的话,元素的类型信息就全部丢失。

    下界类型通配符get方法受限,但可以往列表中添加各种数据类型的对象。因此如果你想把对象写入一个数据结构里,使用 ? super 通配符。限定通配符总是包括自己。

    PECS(Producer Extends Consumer Super)原则

    即:如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符。

    1. 频繁往外读取内容的,适合用上界Extends。
    2. 经常往里插入的,适合用下界Super。

    需要返回T的src是生产者,因此声明为List<? extends T>,需要写入T的dest是消费者,因此声明为List<? super T>。

    使用类似<? super Integer>通配符作为方法参数时表示:

    1. 方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);
    2. 方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();
    展开全文
  • java泛型详解

    万次阅读 多人点赞 2016-04-29 01:02:17
    1、什么是java泛型? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 2、...
  • java泛型

    2019-08-13 21:05:47
    java泛型泛型概述泛型的应用总结普通泛型通配符受限泛型Java泛型无法向上转型Java泛型接口Java泛型方法通过泛型方法返回泛型类型实例使用泛型统一传入的参数类型Java泛型数组Java泛型的嵌套设置 泛型概述 泛型的...
  • 首先说一下java泛型吧,泛型是Java SE 1.5的新特性,用来在编译时做类型检查的,并且会根据泛型类型自动进行类型转换,也就是说,泛型只在编译期起作用,主要是用来保证类型安全的,编译后的class文件中是不会包含...
  • class GenClassDemo4  { public static void main(String[] args)  { SupGen sg=new SupGen(); sg.setVar1(new Dog()); sg.getVar1().tell();...//SupGen sg=new SupGen();.../*GenClassDemo4.java:5: 错误:
  • java泛型-super和exends

    2019-08-31 18:13:51
    java中,没有泛型出现之前,通过定义类型是Object来实现参数的“任意化”。使用Object实现任意化的缺点是需要强制类型转换,而且错误需要在运行期才能发现。而使用泛型的好处是在编译时的时候检查类型安全,并且...
  • java 泛型,java相关文章:java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一Java泛型详解:和Class的使用。泛型类,泛型方法的详细使用实例Java泛型深入理解java泛型你需要知道的一切Java 泛型教程以下基于毕...
  • java泛型面试 Java面试中的通用面试问题在相当长的时间内在Java 5周围越来越普遍,许多应用程序都转移到Java 5上,并且几乎所有新的Java开发都发生在Tiger(Java 5的代号)上。 泛型和Java 5功能(例如Enum)的...
  • Java泛型

    千次阅读 2017-02-25 21:35:34
    Java泛型是JDK1.5加入的新特性。泛型是指参数化的能力。可以定义带泛型的类型的类或者方法,编译时期编译器会用具体的类型来代替它。Java泛型有泛型类、泛型接口和泛型方法。泛型的主要优点是能够在编译时期而不是在...
  • Java 泛型

    千次阅读 2019-02-24 12:04:26
    Java 泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个...
  • Object object = listA.get(0);
  • 自定义泛型:extends,super关键字的使用 1.泛型类的使用 public class ResponseResult<T> {} 2.泛型方法的使用 public static <T extends String> T concat(T... ts) {} 3.泛型接口的使用 public ...
  • Java泛型使用中使用“?”作为类型的通配符。《Effective Java》第28条,使用通配符可以提高API的灵活性,但是通配符也使得泛型的使用变得更加复杂。 泛型类型是不可变首先泛型类型是不可变的,比如List就不是...
  • Java泛型中extends和super

    2019-12-27 19:36:40
    java泛型匹配: <? extends T> 表示类型的上界,表示参数化类型的可能是T或是T的子类. <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型)直至Object; PECS...
  • Java泛型与Kotlin泛型

    2021-04-08 12:14:52
    本文主要列举Java泛型与Kotlin泛型的基本知识,以及两者的区别。 什么泛型 泛型程序设计是程序设计的一种风格或或规范。简单的说就是该类型可变,在编写代码时可以根据情况设置不同的类型。因为泛型的可变性,很容易...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,761
精华内容 21,904
关键字:

java泛型super

java 订阅