十:Java之泛型

十:Java之泛型

大家好,又见面了,我是全栈君。


【定义】

一、泛型的定义主要有下面两种:

       在程序编码中一些包括类型參数的类型,也就是说泛型的參数仅仅能够代表类。不能代表个别对象。(这是当今较常见的定义)

在程序编码中一些包括參数的类。其參数能够代表类或对象等等。

(如今人们大多把这称作模板)

       不论使用那个定义。泛型的參数在真正使用泛型时都必须作出指明。

      泛型类。是在实例化类的时候指明泛型的详细类型。

      泛型方法。是在调用方法的时候指明泛型的详细类型。

二、使用泛型的目的:

      一些强类型程序语言支持泛型,其主要目的是加强类型安全及降低类转换的次数,但一些支持泛型的程序语言仅仅能达到部份目的。

泛型程序设计(Genericprogramming)意味着编写的代码能够被非常多不同类型的对象所重用。

是对java语言的类型系统的一种扩展,以支持创建能够按类型进行參数化的类。能够把类型參数看作是使用參数化类型时指定的类型的一个占位符。就像方法的形式參数是执行时传递的值得占位符一样。

【Java泛型的几种类型代码】

 

一、不使用泛型的代码

 

     我们定义一个Person类,包括三个属性x。y,z。在我们開始定义地时候,我们也不知道这三个属性是用来干什么的,所以我们定义为Object类型。

可是在使用的时候,我们分别对x。y。z赋予了int,double。String类型,所以在取出的时候。我们须要把这三个类型值进行强制转换。例如以下代码:

 

   1. Person.java

<span style="font-size:18px;"> public class Person {
     private Object x;
     private Object y;
     private Object z;
      //使用Object类型。能够转化为不论什么类型
     public Object getX() {
         return x;
      }
     public void setX(Object x) {
        this.x = x;
    }
    public Object getY() {
        return y;
    }
    public void setY(Object y) {
        this.y = y;
    }
    public Object getZ() {
        return z;
    }
    public void setZ(Object z) {
        this.z = z;
    }
 }

2. NoGenericTest.java
 

 public class NoGenericTest {
     public static void main(String[]args){
         Person boy=new Person();
         boy.setX(20);
         boy.setY(22.2);
         boy.setZ("帅哥TT");
         //这里依据设置的不同类型的值,我们须要进行强制类型转化。

int x=(Integer)boy.getX(); double y=(double)boy.getY(); String z=(String)boy.getZ(); System.out.println(x); System.out.println(y); System.out.println(z); }}3. 执行结果 20 22.2 帅哥TT</span>

二、使用一个类型变量泛型的代码

 

     我们定义一个泛型类Person,定义三个属性x,y,z,在測试类中,我们设置属性的值,并打印。

<span style="font-size:18px;">  1. Person.java
 public class Person<T> {
    private T x;
     private T y;
    private T z;
     public T getX() {
          return x;
      }
     public void setX(T x) {
         this.x = x;
    }
    public T getY() {
        return y;
    }
    public void setY(T y) {
        this.y = y;
    }
    public T getZ() {
        return z;
    }
    public void setZ(T z) {
        this.z = z;
    }
 }

2. GenericTest.java
 
 public class GenericTest {
    public static void main(String[]args){
         Person boy=new Person();
         boy.setX(20);
         boy.setY(22.2);
         boy.setZ("帅哥TT");
         //不用进行类型转化
          System.out.println(boy.getX());
         System.out.println(boy.getY());
        System.out.println(boy.getZ());
    }
 }

3. 执行结果
20
22.2
帅哥TT
三、使用两个类型变量泛型的代码
     我们定义一个泛型类Person,定义两个属性x,y。使用了两种不同的类型变量,在測试类中。我们设置属性的值。并打印。
 
1. Person.java
 
 public class Person<T1,T2> {
     private T1 x;
     private T2 y;
     public T1 getX() {
         return x;
      }
     public void setX(T1 x) {
         this.x = x;
      }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
       this.y = y;
    }
}

2. GenericTest.java
 
public class GenerricTest {
     public static void main(String[] args){
         Person<String,Integer> boy=new Person<String,Integer>();
         boy.setX("帅哥TT");
         boy.setY(20);
         System.out.println(boy.getX());
        System.out.println(boy.getY());
      }
 
 }
 3. 执行结果
  帅哥TT
  20</span>

四、使用泛型的继承

     我们定义一个泛型类Person,定义两个属性x。y,然后定义还有一个泛型类Boy。定义属性z。Boy继承Person类,在測试类中,我们设置属性的值。并打印。

 

  

 1. Person.java
 
 public class Person<T1,T2> {
     private T1 x;
     private T2 y;
     public T1 getX() {
         return x;
      }
     public void setX(T1 x) {
         this.x = x;
      }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
        this.y = y;
    }
}

 2. Boy
 
 public class Boy<T1,T2,T3>extendsPerson<T1,T2> {
   private T3 z;
    public T3 getZ() {
        return z;
    }
    public void setZ(T3 z) {
        this.z = z;
    }
 }

3. GenericTest.java
 

 1public class GenericTest {
 2    public static void main(String[] args){
 3        Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
 4        boy.setX("帅哥TT");
 5        boy.setY(20);
 6        boy.setZ(200000.22);
 7        
 8        System.out.println(boy.getX());
 9        System.out.println(boy.getY());
10        System.out.println(boy.getZ());
11    }
12 }

 4.执行结果
1 帅哥TT
2 20
3 200000.22

五、使用泛型的接口

     我们定义一个泛型接口Person,定义两个方法,然后定义还有一个泛型类Boy,实现泛型接口Person。定义属性x,y,z,在測试类中。我们设置属性的值,并打印。


<span style="font-size:18px;">1. Person.java

1 public interface Person<T1,T2> {
2    public T1 getX();
3    public T2 getY();
4 }
 2. Boy

 1public class Boy<T1,T2,T3>implements Person<T1,T2> {
 2    private T1 x;
 3    private T2 y;
 4    private T3 z;
 5    public T1 getX() {
 6        return x;
 7     }
 8    public void setX(T1 x) {
 9        this.x = x;
10    }
11    public T2 getY() {
12        return y;
13    }
14    public void setY(T2 y) {
15        this.y = y;
16    }
17    public T3 getZ() {
18        return z;
19    }
20    public void setZ(T3 z) {
21        this.z = z;
22    }
23
24 }

3. GenericTest.java
 
 1public class GenericTest {
 2    public static void main(String[] args){
 3        Boy<String,Integer,Double> boy=newBoy<String,Integer,Double>();
 4        boy.setX("帅哥TT");
 5        boy.setY(20);
 6        boy.setZ(200000.22);
 7        System.out.println(boy.getX());
 8        System.out.println(boy.getY());
 9        System.out.println(boy.getZ());
10    }
11 }

4. 执行结果
1 帅哥TT
2 20
3 200000.22</span>

六、使用泛型方法

     说明一下。定义泛型方法时,必须在返回值前边加一个<T>。来声明这是一个泛型方法,持有一个泛型T,然后才干够用泛型T作为方法的返回值。

     定义一个普通类Person,定义一个泛型方法,例如以下代码:

<span style="font-size:18px;">1. Person.java

 1public class Person{
 2    public static<T>T getMiddle(T[]a){
 3        return a[a.length/2];
 4     }
 5    public static void main(String [] args){
 6        String[]name={"帅哥TT","帅哥TT1","帅哥TT2"};
 7        String middle=Person.<String>getMiddle(name);
 8        System.out.println(middle);
 9        
10        Integer[]num={20,22,25};
11        Integer middle1=Person.<Integer>getMiddle(num);
12        System.out.println(middle1);
13        
14        Double[]num1={20.0,22.2,25.5};
15        Double middle2=Person.<Double>getMiddle(num1);
16        System.out.println(middle2);
17    }
18 }

2. 执行结果

1 帅哥TT1
2 22
3 22.2</span>

七、类型变量的限定

     例如以下代码,我们在方法min中定义了一个变量smallest类型为T。这说明了smallest能够是不论什么一个类的对象,我们在以下的代码中须要使用compareTo方法, 可是我们没有办法确定我们的T中含有CompareTo方法,所以我们须要对T进行限定,在代码中我们让T继承Comparable类。

例如以下:

<span style="font-size:18px;">public static<T extendsComparable>T min(T[]a)
   1.Person.java
 1public class Person{
 2    public static<T extends Comparable>T min(T[]a){
 3        if(a==null||a.length==0){
 4            return null;
 5        }
 6        T smallest=a[0];
 7        for(int i=1;i<a.length;i++){
 8            if(smallest.compareTo(a[i])>0){
 9                 smallest=a[i];
10             }
11        }
12        return smallest;
13    }
14    public static void main(String [] args){
15        Integer[]num={20,25,30,10};
16        Integer middle=Person.<Integer>min(num);
17        System.out.println(middle);
18    }
19 }

 2. 执行结果
 10</span>

【Java泛型理解】

一、类型擦除

     正确理解泛型概念的首要前提是理解类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。

在生成的Java字节代码中是不包括泛型中的类型信息的。使用泛型的时候加上的类型參数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型。在编译之后都会变成List。JVM看到的仅仅是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,可是仍然无法避免在执行时刻出现类型转换异常的情况。

     非常多泛型的奇怪特性都与这个类型擦除的存在有关,包含:

     泛型类并没有自己独有的Class类对象。比方并不存在List<String>.class或是List<Integer>.class,而仅仅有List.class。

静态变量是被泛型类的全部实例所共享的。对于声明为MyClass<T>的类,訪问当中的静态变量的方法仍然是 MyClass.myStaticVar。

无论是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。

泛型的类型參数不能用在Java异常处理的catch语句中。

由于异常处理是由JVM在执行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException<String>和MyException<Integer>的。对于JVM来说,它们都是 MyException类型的。也就无法执行与异常相应的catch语句。

二、最佳实践

        在使用泛型的时候能够遵循一些主要的原则。从而避免一些常见的问题。

在代码中避免泛型类和原始类型的混用。比方List<String>和List不应该共同使用。这样会产生一些编译器警告和潜在的执行时异常。当须要利用JDK 5之前开发的遗留代码,而不得不这么做时,也尽可能的隔离相关的代码。

在使用带通配符的泛型类的时候,须要明白通配符所代表的一组类型的概念。因为详细的类型是未知的,非常多操作是不同意的。

       泛型类最好不要同数组一块使用。你仅仅能创建new List<?>[10]这种数组。无法创建new List<String>[10]这种。这限制了数组的使用能力。并且会带来非常多费解的问题。因此,当须要类似数组的功能时候,使用集合类就可以。

不要忽视编译器给出的警告信息。

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/116078.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • MySQL 之全文索引「建议收藏」

    MySQL 之全文索引「建议收藏」最近在复习数据库索引部分,看到了fulltext,也即全文索引,虽然全文索引在平时的业务中用到的不多,但是感觉它有点儿意思,所以花了点时间研究一下,特此记录。引入概念通过数值比较、范围过滤等就可以完成绝大多数我们需要的查询,但是,如果希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较。全文索引就是为这种场景设计的。你可能会说,用like…

    2022年6月21日
    28
  • 线程池详解(通俗易懂超级好)「建议收藏」

    线程池详解(通俗易懂超级好)「建议收藏」目标【理解】线程池基本概念【理解】线程池工作原理【掌握】自定义线程池【应用】java内置线程池【应用】使用java内置线程池完成综合案例线程池线程池基础线程池使用线程池综合案例4.学员练习5.线程池总结概念介绍1:什么是线程池2:为什么使用线程池3:线程池有哪些优势什么是池什么是线程池线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runna.

    2025年8月3日
    4
  • docker 导出所有镜像[通俗易懂]

    docker 导出所有镜像[通俗易懂]docker导出所有镜像

    2025年9月20日
    6
  • 关于去掉Li标签前面的小圆点和距离/显示下划线

    关于去掉Li标签前面的小圆点和距离/显示下划线解决方法去掉 Li 标签前面的距离 nbsp nbsp 设置 ul nbsp nbsp padding 0px 去掉 Li 标签前面的小圆点 设置 li nbsp nbsp nbsp list style type none 显示下划线 nbsp nbsp nbsp text decoration underline

    2025年7月24日
    5
  • 1.网络工具:ifconfig,ping,netstate,Redhat命令和图形化设置ip,finger,nslookup

    1.网络工具:ifconfig,ping,netstate,Redhat命令和图形化设置ip,finger,nslookup

    2022年1月13日
    51
  • Java JDK 下载安装,以及环境配置

    Java JDK 下载安装,以及环境配置一、JavaJDK下载1)登录oracle官网2)选择Menu\Java\JavaSE3)向下滚动页面,找到GetStarted\DownloadJavaSEforDevelopers4)点击JDK下载5)同意协议AcceptLicenseAgreement\下载对应JDK版本,我的是Windows系统,所以选择Windowsjdk-9.0.4最新版本二、…

    2022年7月7日
    21

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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