java11-泛型及其使用[通俗易懂]

java11-泛型及其使用[通俗易懂]1.概述就本质而言“泛型”的意思就是参数化类型。参数化类型很重要,因为使用该特性创建的类、接口以及方法可以以参数的形式指定操作的数据类型。泛型通俗的说就是方法的返回值或参数是不确定的,可以随创建

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

1.概述

    就本质而言 “泛型”的意思就是参数化类型。参数化类型很重要,因为使用该特性创建的类、接口以及方法可以以参数的形式指定操作的数据类型。

    泛型通俗的说就是方法的返回值或参数是不确定的,可以随创建该类对象时改变而改变。

    泛型提供了以前缺失的类安全性,并且还可以简化处理过程(例如避免进行强制类型转换等),提高了代码的可重用性。

2.一段简单的代码理解泛型

    对于下面这段代码我们定义了一个泛型类,类中有一个私有的泛型成员变量和几个泛型方法。从中我们可以看出,成员变量或者方法的参数或者返回值可能不是肯定的而是T,并且T是可变的,我们称这个T为泛型。

    main方法中分别使用这个泛型类操作了String 和 Integer 。如果没有泛型的支持,我们就只能写两个逻辑相同而参数不同的类或者进行强制转换,大大降低了代码重用和安全性。

 1 class MyGeneric<T>{ //声明一个泛型类
 2     private T obj;//可以使用泛型指定变量
 3     public MyGeneric() {}; //无参构造
 4     public MyGeneric(T val) { //泛型构造
 5         this.obj = val;
 6     }
 7     public T getObj() { //返回值
 8         return this.obj;
 9     }
10     public String getName() { //得到确切类型
11         return this.obj.getClass().getName();
12     }
13     
14 }
15 
16 public class TestGeneric {
17     public static void main(String[] args) {
18         MyGeneric<String> str = new MyGeneric<String>("xiaobai"); //操作String 
19         System.out.println(str.getObj());
20         System.out.println(str.getName());
21         
22         MyGeneric<Integer> integer = new MyGeneric<Integer>(5); //操作Integer 
23         System.out.println(integer.getObj());
24         System.out.println(integer.getName());
25     }
26 }

3.几点注意

    1.泛型只是一个占位符,并没有实际意义,对于上面的例子,实际上操作的还是相应的数据类型(String和Integer)

    2.泛型只能使用引用类型,不支持基本数据类型(至于上面第22行可以直接写5是因为自动装箱)

    3.泛型引用之间是不兼容的,比如上面例子中的代码 str和integer两个实例对象是不兼容的(这是废话,泛型的目的就是提高通用性并限制操作类型)

4.多个泛型

    java中允许一个类有多个泛型,泛型之间使用逗号隔开即可(参看Map<K,V>)。并且泛型只是一个占位符,可以用任何字符表示

    

 1 class ManyGeneric<T,V,W>{
 2     private T obj1;
 3     private V obj2;
 4     private W obj3;
 5     public T getObj1() {
 6         return obj1;
 7     }
 8     public void setObj1(T obj1) {
 9         this.obj1 = obj1;
10     }
11     public V getObj2() {
12         return obj2;
13     }
14     public void setObj2(V obj2) {
15         this.obj2 = obj2;
16     }
17     public W getObj3() {
18         return obj3;
19     }
20     public void setObj3(W obj3) {
21         this.obj3 = obj3;
22     }
23     
24 }

5.泛型方法

    如果我们只想在一个方法中使用泛型,换句话说:如果类或者接口没有定义泛型,但是其中的某个或几个方法需要使用泛型(比如静态方法接受一个泛型参数等)该怎么解决? 答案就是泛型方法。

    定义泛型方法的语法是: 访问修饰符 <泛型参数列表> 方法返回值 方法名称(形参列表) {  方法体  }

    eg: public static <T> mygeneric(T t) {xxx}  

    泛型方法的定义和普通方法定义不同的地方在于需要在修饰符和返回类型之间加一个泛型类型参数的声明,表明在这个方法作用域中谁才是泛型类型参数;

    泛型方法与泛型类的区别:

      

class A<T>{
          //泛型的作用域是整个类 
    }

  

class A{
     
     public static <T> print(T t){   //该泛型只作用于该方法上   
           sout(t);
     }    
         
}

 

6.泛型有界类型及泛型通配

  6.1 泛型通配

    不显式第指定泛型类型,而是使用 ?来表示泛型。当我们无法立即确定该类引用使用哪一个泛型(或至少可以确定他与某一个类的关系时)就使用这种形式。该通配一共有三种形式

      1.  <?> 形式: 不知道泛型到底是何形式·:使用这种方式时  就相当一  <? extends  Object  >  引用的实际对象只要是Object的子类就可以 

      

ArrayList<?> list = new ArrayList<String> (); //正确  
ArrayList<?> list = new ArrayList<Integer> (); //正确  

      2. <? extends xxx> 不知道具体是哪一种形式,但一定是xxx的子类  :引用的实际对象只能是 xxx类或xxx类的子类

ArrayList<? extends Number> list = new ArrayList<String> (); //错误 不是Number子类
ArrayList<? extends Number> list = new ArrayList<Integer> (); //正确  

      3. <? super xxx> 不确定哪一种形式,但一定是xxx的父类:引用的实际对象只能是  xxx 或 xxx的父类

ArrayList<? super Integer> list = new ArrayList<Number> (); //正确  
ArrayList<? super Integer> list = new ArrayList<Object> (); //正确 
ArrayList<? super Integer> list = new ArrayList<String> (); //错误 不是Integer的父类 

 

  6.2 有界类型

    可以使用任意参数来替换泛型类型是很好的,但是有一些时候我们可能希望对泛型类型进行一些限制。例如我们想在泛型中取得每一个值得double值,这个只有在类继承Number类才可以实现,否则就会编译错误。

    那么这个时候我们就可以使用有界类型进行限定,要求泛型类型必须是Number类或其子类。

    语法: T extends superClass  意思是说只允许superClass或者其子类的泛型。

见如下例子:

    错误的代码:

 1 class MyMathClass<T>{
 2     T[] nums;
 3     public MyMathClass(T[] o) {
 4         this.nums = o;
 5     }
 6     public double avg() {
 7         double sum = 0.0;
 8         for(int i=0;i<nums.length;i++) {
 9             sum += nums[i].doubleValue();  //报错误
10         }
11         return sum/nums.length;
12     }
13 }
14 
15 public class TestGeneric {
16     public static void main(String[] args) {
17         Integer[] nums = new Integer[10];
18         for(int i=0;i<10;i++) {
19             nums[i] = (int)(Math.random()*100);
20         }
21         MyMathClass<Integer> test = new MyMathClass<Integer>(nums);
22         test.avg();
23     }
24 }

    这里的代码第9行报错,因为不是所有的类型都有doubleValue方法。但是只要是继承了Number类就会有这个方法,所以我们使用有界类型进行限定

    限定方式  T extends Number

 1 class MyMathClass<T extends Number>{
 2     T[] nums;
 3     public MyMathClass(T[] o) {
 4         this.nums = o;
 5     }
 6     public double avg() {
 7         double sum = 0.0;
 8         for(int i=0;i<nums.length;i++) {
 9             sum += nums[i].doubleValue();  //报错误
10         }
11         return sum/nums.length;
12     }
13 }
14 
15 public class TestGeneric {
16     public static void main(String[] args) {
17         Integer[] nums = new Integer[10];
18         for(int i=0;i<10;i++) {
19             nums[i] = (int)(Math.random()*100);
20         }
21         MyMathClass<Integer> test = new MyMathClass<Integer>(nums);
22         System.out.println(test.avg());
23     }
24 }

    同理还可以使用多边界限定,当有多个边界的时候 使用&进行连接 :class MyMathClass<T extends Number & Serializable> 意思就是说只允许Number或其子类类型并且必须实现Serializable接口。

    注意有界类型与泛型通配的区别 (有界类型是声明泛型类指定泛型范围,而泛型通配是泛型引用指向泛型对象时的限制)

7.泛型擦除

    由于要与以前的代码相兼容,java中的泛型是伪泛型,在编译器编译过程中将会擦除泛型的所有信息,使用确切的引用类型将其代替。这就意味着如果没有显示的确定类型,就将使用Object代替,然后进行强制转换来保证类型的匹配

    这里就会发生一个模糊性问题,当有两个重载方法时,并且该方法唯一区别就是参数类型不同,则可能造成错误:

  java11-泛型及其使用[通俗易懂]

8. 使用泛型的一些限制

    8.1 不能实例化泛型参数

         T  obj = new T(); 是不合法的,因为编译器不知道要创建哪一种类型的对象,T只是一个占位符。

    8.2 静态成员限制

        静态成员不能使用泛型声明参数,同时,静态方法也不能操作泛型参数,因为静态属性需要随类加载,无法判断当前泛型的类型。

    8.3 数组的限制

        1.不能实例化泛型数组  原因参考7.1  

        2.不能声明指向特定泛型类型的数组

        java11-泛型及其使用[通俗易懂]

 

9.泛型对异常的限制

    泛型类不能继承Throwable类,这就意味着不能创建泛型异常类(也就是说泛型不支持异常,但是泛型类中依旧支持其他类型的异常)

    当然,泛型支持异常也没有什么意义。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/167912.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 最大子段和

    最大子段和

  • C语言如何生成随机数

    C语言如何生成随机数生成10个100以内的随机数废话不多说直接上程序。#include&lt;stdio.h&gt;#include&lt;stdlib.h&gt;#include&lt;time.h&gt;intmain(){intret=0;srand((unsignedint)time(NULL));for(inti=0;i&lt;10;i…

  • Lambda plus: 云上大数据解决方案

    Lambda plus: 云上大数据解决方案本文会简述大数据分析场景需要解决的技术挑战,讨论目前主流大数据架构模式及其发展。最后我们将介绍如何结合云上存储、计算组件,实现更优的通用大数据架构模式,以及该模式可以涵盖的典型数据处理场景。大数据处理的挑战现在已经有越来越多的行业和技术领域需求大数据分析系统,例如金融行业需要使用大数据系统结合VaR(valueatrisk)或者机器学习方案进行信贷风控,零售、餐饮行业需要大数据系统…

  • python的多线程是否没有用了[通俗易懂]

    python的多线程是否没有用了[通俗易懂]python的多线程是否就完全没有用了呢?相同的代码,为何有时候多线程会比单线程慢,有时又会比单线程快? 这主要跟运行的代码有关: 1、 CPU密集型代码 (

  • 开源!!!100 多个常用 API 数据接口免费分享!建议收藏![通俗易懂]

    开源!!!100 多个常用 API 数据接口免费分享!建议收藏![通俗易懂]点击上方“Java精选”,选择“设为星标”别问别人为什么,多问自己凭什么!下方有惊喜留言必回,有问必答!每天08:15更新文章,每天进步一点点…我们在开发的过程中,常常调用API接口,往往事半功倍。今天给大家整理了优秀的API接口!各类无次数限制的免费API接口整理,主要是聚合数据上和APIStore上的一些,还有一些其他的。聚合数据提供30大类,160种以上基…

  • MixMatch论文学习笔记

    MixMatch论文学习笔记项目内容论文名MixMatch:AHolisticApproachtoSemi-SupervisedLearning作者DavidBerthelot,NicholasCarlini,IanGoodfellow,AvitalOliver,NicolasPapernot,ColinRaffel主要内容发表时间2019年Abstract

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号