泛型与类型参数

前言

  1. 参考:
    1. CSDN-java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
    2. 《Java核心技术 卷1》第8章 泛型程序设计

介绍

  1. 定义:JDK1.5定义了泛型,即参数化类型,将类型参数应用到不同的类、接口、方法中,将类型作为参数进行传递,在使用时传入具体类型。
  2. 从Java程序设计语言1.0版本发布以来,变化最大的部分就是泛型。JDK5增加泛型机制的主要原因是为了满足JDK5之前的Java规范
  3. 使用泛型机制编写的程序代码要比杂乱地使用Object变量,然后再进行强制类型转换的程序代码具有更好的安全性和可读性。泛型对集合类尤其有用。
  4. 另外泛型编写的代码可以被很多不同类型的对象所重用,例如一个ArrayList类就可以聚集任何类型的对象。
  5. 类型参数的好处:在引入类型参数之前我们一般使用Object,有两个问题,一是获取一个值的时候必须进行强制类型转换,二是没有类型检查,可能编译和运行的时候都不会出错,但是在使用元素的时候就会出错。泛型提供了一个更好的解决方案,就是类型参数,另外通过类型参数来指示元素的类型,这使得代码具有更好的可读性,例如ArrayList。
    1. 当调用get的时候不需要进行类型强制转换,编译器就知道返回值类型,例如ArrayList<String>调用get时返回值类型为String,而不是Object。
    2. 另外还可以检查插入的值类型是否错误,比如编译器知道ArrayList中add方法中有一个String类型的参数,这比使用Object类型的参数安全一些。
    3. 总结,类型参数的魅力在于,使得程序具有更好的可读性和安全性
  6. 补充:
    1. 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
    2. 不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错:
if(ex_num instanceof Generic<Number>){}

特性

  1. 泛型只在编译阶段有效。
  2. 泛型是通过类型擦除实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息,例如List<String>在运行时仅用一个List来表示。这样做的目的,是确保能和Java5之前的版本兼容。
  3. 类型擦除:泛型信息值存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,会被替换为限定类型,之前泛型类中的类型参数部分如果没有指定上限,则会被转译成普通的Object类型。
  4. 虚拟机没有泛型类型对象,所有对象都属于普通类。
  5. 泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同的基本类型,比如List<String>List<Integer>,实际上编译之后都是类型List,举例:
import java.util.ArrayList;
import java.util.List;

public class Main {
   public static void main(String[] args) {
      List<String> stringList = new ArrayList<>();
      List<Integer> integerList=new ArrayList<>();
      if((stringList.getClass()).equals(integerList.getClass())){
         System.out.println("类型相同");
      }
   }
}
/**
 * 输出结果:类型相同 
 */
  1. 泛型中的限定通配符和非限定通配符:限定通配符对类型进行了限制,有两种,一种是<? extends T>,它通过确保类型必须是T的子类来设定类型的上线,另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下届。另一方面<?>表示非限定通配符,它可以用任意类型来替代。
    1. 泛型类型必须用限定内的类型来初始化,否则会导致编译错误。
  2. 补充:
    1. 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的,它们之间也没有任何关系,只是都是相同的基本类型罢了,但是不能相互转换。

泛型的使用

  1. 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法
  2. 类型变量使用大写形式,且比较短。在Java库中,使用变量E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T(需要时还可以用临近的字母U和S)表示“任意类型”。

泛型类

  1. 泛型类:就是具有一个或多个类型变量的类。
  2. 泛型类可看作普通类的工厂。
  3. 举例,一个普通的泛型类:
/**
 * 在实例化泛型类时,必须指定T的具体类型
 * @param <T>
 */
public class Generic<T>{
    //T的类型有外部指定
    private T key;
    
    public Generic(T key){
        this.key=key;
    }
    
    public T getKey(){
        return key;
    }
}

泛型接口

  1. 泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
public interface Generator<T>{
    public T next();
}
  1. 当实现泛型接口的类,未传入泛型实参时:
public class FruitGenerator<T> implements Generator<T>{
    @Override
   public T next(){
        return null;
    }
}
  1. 当实现泛型接口的类,传入泛型实参时:
public class FruitGenerator implements Generator<String>{
    @Override
   public String next(){
        return null;
    }
}

泛型方法

  1. 在Java中,泛型类的定义非常简单,泛型方法就比较复杂了。泛型类中很多成员方法使用了泛型,很多泛型类中也包含着泛型方法。
  2. 泛型方法的基本介绍:
    1. public与返回值中间<T>(放在关键字后面,返回类型的前面)非常重要,可以理解为声明此方法为泛型方法。
    2. 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
    3. <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。

泛型方法与可变参数

  1. 举例:
public class Main{
    public <T> void printMsg(T... args){
        for(T t:args){
           System.out.println(t);
        }
    }

   public static void main(String[] args) {
      printMsg("111",222,"aaaa","2323.4",55.55);
   }
}
/*
111
222
aaaa
2323.4
55.55
*/

静态方法与泛型

  1. 静态方法有一种情况需要注意一下,就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型,如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。
public class StaticGeneric<T>{
    //静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。
    public static <T> void shot(T t){}
}

泛型方法总结

  1. 泛型方法能使方法独立于类而产生变化。只有声明了<T>的才是泛型方法。

关于泛型数组

  1. 在Java中是”不能创建一个确切的泛型类型的数组“,但是使用通配符创建泛型数组是可以的。因为对于通配符的方式,最后取出数据是要做显式的类型转换的。
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        //不可以
        List<String>[] ls = new ArrayList<>[10];
        //以下可以
        List<?>[] ls=new ArrayList<?>[10];
        List<String>[] ls=new ArrayList[10];
    }
}

补充

  1. 定义形参,传入实参。
  2. 桥方法保持多态。

面试重点

  1. 泛型定义:JDK1.5定义了泛型,即参数化类型,将类型参数应用到不同的类、接口、方法中,将类型作为参数进行传递,在使用时传入具体类型。
  2. 为什么要使用泛型(泛型优点):
    1. 在引入类型参数之前我们一般使用Object,有两个问题,一是获取一个值的时候必须进行强制类型转换,二是没有类型检查,可能编译和运行的时候都不会出错,但是在使用元素的时候就会出错。
    2. 引入泛型之后使程序具有了更好的可读性和安全性。
      1. 可读性:不需要强制转换,编译器就知道返回值类型。另外不需要杂乱地使用Object变量。
      2. 安全性:增加了类型检测,比如List<String>在插入元素的时候就会提示错误,不会等到获取/使用元素的时候才出错。
  3. 泛型特性
    1. 泛型只在编译阶段有效,在虚拟机中对象只有普通对象,在编译时会进行类型擦除,擦除与泛型有关的信息,将泛型用限定类型替代,如果没有上限,则可用Object替代。
    2. 限定修饰符包含两种,<? extends T>和<? super T>,一个指定上界,一个指定下界;非限定修饰符<?>可以表示任何类型,用Object替代。
  4. 泛型使用
    1. 泛型有三种使用方式,分别是:泛型类、泛型接口、泛型方法。
    2. 类型参数用大写字母表示,Java库中,E表示集合的元素类型、K,V表示键与值的类型、T(U,S)等表示任意类型。
  5. 总结:
    1. 上面总结的基本够了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值