【JAVA学习之路 | 进阶篇】<-泛型->

简介: 【JAVA学习之路 | 进阶篇】<-泛型->

1.泛型的概念

所谓泛型,就是允许在定义类, 接口 时通过一个"标识"表示类中某个属性的类型或者某个方法的返回值或形参类型.这个类型参数将在使用时确定.

2.举例

(1). 集合类在设计阶段/声明阶段不能确定这个容器到底存的是什么对象,所以在JDK5.0版本以前,只能把元素类型设计为Object类.JDK5.0时java引入了参数化类型的概念,允许我们在创建集合时指定集合元素的类型.(如List<String>表示该List只能存放String类型的数据.)


使用集合存储数据时,除了元素的类型不确定,其他部分都是确定的.


(2). java.lang.Comparable接口和java.util.Comparator接口,是用于比较对象大小的接口.这两个接口只是限制了当一个对象大于另一个对象时返回正整数,等于返回0,否则返回负整数.但并不确定是什么类型的对象比较大小.jdk5.0之前只能用Object类型表示,即麻烦(可能需要强制类型转换)又不安全(可能比较的两个对象并不是同一类型).故jdk5.0给它们增加了泛型.

3.集合在未使用泛型前可能存在的问题

  • 类型不安全.add(Object obj)形参类型是Object,意味着任何类型的对象都可以被放入到集合中.
  • 需要强转操作,繁琐.还可能出现异常.

4.使用说明

(1). 我们在声明完自定义泛型类后,可以在类的内部(如属性,构造器,方法)使用类的泛型.

public class Order<T>{//泛型类
    T t;//属性
 
    public Order() {
    }
 
    public Order(T t) {//构造器
        this.t = t;
    }
    public T show() {
        return t;//方法
    }
}

(2). 我们在创建自定义泛型类的对象时,可以指明泛型参数类型.一旦指明,内部凡是使用类的泛型参数的位置,都具体化为指定的类的泛型类型.


(3). 如果在创建自定义泛型类的对象时,没有指明泛型参数类型,那么泛型将被擦除,泛型对应的类型都按照Object处理,但不等价于Object.


下列是ArrayList的源码,也涉及到了泛型.

(4). 泛型的指定类型必须使用引用数据类型,不能使用基本数据类型,此时只能使用包装类.


Order<int> o = new Order<>();//错    Order<Integer> o = new Order<>();//正确


(5). 除了创建泛型类的对象外,子类继承泛型类时,实现类实现泛型接口时,也可以确定泛型结构的泛型参数.如果我们在给泛型提供子类时,子类也不确定泛型的类型,则继续可以使用泛型参数,我们还可以在现有父类的泛型参数的基础上,新增泛型参数.

public class SubOrder<T1, T2> extends Order<String>{
    T1 data1;
    T2 data2;
}

5.注意事项

(1). 泛型类可能有多个参数,可以将多个参数都放到尖括号内,如(<T1, T2, T3>).


(2). 从jdk7.0开始,可以简化泛型操作 :


Order<String> o =new Order<String>();//以前


Order<String> o =new Order<>()//jdk7.0之后.


(3). 如果泛型结构是一个接口或抽象类,则不可以创建对象.//这里其实是接口/抽象类的性质.


(4). 不能使用new T[](因为此时new对象的时候并不知道T的类型,所以无法为其分配内存),但可以使用


T[] element =(T[]) new Object[10];//强制类型转换


(5). 在类/接口上声明的泛型,在本类或本接口中代表某种类型,但不可以在静态方法中使用泛型.(原因也很简单,因为静态方法随着类的加载而加载,而泛型的类型等到创建对象时才能确定,此时泛型的类型还是未知的)


(6). 异常类是不可以带泛型的.

6.泛型方法

这些并不是泛型方法,因为此时的T是泛型类的泛型参数.

//这些都不是泛型方法
public T method3() {
        //方法体
}
public void method4(T t) {
        //方法体
}

正确写法

权限修饰符 <E> 返回值类型 method(参数列表){

    //方法体

   //<E>表明E是泛型方法的泛型参数.

}

例 :

public class GenericityMethhodTest<T> {
    public static <E> void method1(E[] e, int a, int b) {
        E temp;
        temp = e[a];
        e[a] = e[b];
        e[b] = temp;
    }
    public static <E> void method2(E[] e) {
        int j = e.length - 1;
        for (int i = 0; i < (e.length) / 2; i++) {
            E temp;
            temp = e[i];
            e[i] = e[j];
            e[j] = temp;
            j--;
        }
    }
 
    public static void main(String[] args) {
        Integer[] array =  {1, 2, 3, 4, 5};
        method1(array, 0, 4);
        for (Integer i : array) {
            System.out.println(i);
        }
        System.out.println("******************");
        method2(array);
        for (Integer i : array) {
            System.out.println(i);
        }
    }
    
}

说明 :


  • 声明泛型方法时,一定要添加泛型参数<E>.
  • 泛型方法在调用时,需要指明其具体的类型.
  • 泛型类型是可以根据需要来声明为static.则需要在通过类调用该静态方法时指明泛型参数类型.
  • 泛型方法所属的类是否是一个泛型类,其实都可以,因为可能泛型方法的泛型参数与泛型类的泛型参数并不一样.

7.泛型在继承性上的体现

(1). 类superA是A的父类,则G<superA>与G<A>的关系.

例:

public class Genericity {
    public static void main(){
        G<String> g1 = new G<>();
        G<Object> g2 = new G<>();
        //如果二者是有子父类关系,则可以通过多态体现
        //此处报错,说明G<String>与G<Object>不具有子父类关系
        //实际上,二者是不同的,同一个级别的类
        //g2 = g1;
    }
}
class G<E>{
    int age;
    E name;
}

(2). 类superA是A的父类,则superA<E>与A<E>之间的关系.

例 :

public class Genericity {
 
 
    public static void main(String[] args) {
        G<String> g3 = null;
        T<String> g4 = null;
        //G是T的父类
        //此处没有报错,说明G<String>是T<String>的父类
        g3 = g4;
    }
}
class G<E>{
    int age;
    E name;
}
class T<E> extends G<E>{
 
}

8.通配符?

(1). 在阅读源码过程中,经常会看到通配符的使用.

举例 :

G<?> g1 = null;
G<String> g2 = null;
//父类的引用指向子类的对象,即可以将G<?>看作是G<String>类型的父类,
g1 = g2;

(2). 读写数据的特点(以集合ArrayList为例)

ArrayList<?>可以看作是ArrayList<A>的父类

范围 : (-∞, +∞)

        ArrayList<?> a1 = null;
        ArrayList<String> a2 = new ArrayList<>();
        //父类的引用指向子类的对象
        a1 = a2;
        a2.add("AAA");
        a2.add("BBB");
        a2.add("CCC");
        //a1可以通过get方法获取值
        //说明读取数据是允许的
        Object o1 = a1.get(0);
        //而且读取的值的类型为Object类型
        Object o2 = a1.get(1);
 
        //此处报错,说明写入数据是不允许的,但可以有特例
        //a1.add("DDD");
        //可以写入null;
        a1.add(null);
        for(Object i : a1) {
            System.out.println(i);
        }
 
控制台
AAA
BBB
CCC
null

(3). 有条件限制的通配符

比如

List<? extends A> : (-∞, A]

List<? super A> : [A, +∞]

例 : A是B的父类,测试List<? extends A>

@Test
    public void test() {
        List<? extends A> l1 = null;
        List<A> l2 = new ArrayList<>();
        l1 = l2;
        l2.add(new A());
        l2.add(new B());
        //但可以进行可读操作,且get()方法返回值的类型是A,而集合中的元素为A或A的子类的对象,可体现多态性
        A a1 = l1.get(0);
        A a2 = l1.get(1);
        //报错,List<? extends A>不可以进行可写操作,但可以添加nill
//        l1.add(new A());
//        l1.add(new B());
        l1.add(null);
    }

测试List<? super A>

@Test
    public void test2() {
        List<? super A> l1 = null;
        List<A> l2 = new ArrayList<>();
        l2.add(new A());
        l2.add(new B());
        //未报错,说明List<? super A>可以进行可读操作
        //而且get()返回值的类型是Object
        Object object = l1.get(0);
        //未报错,可以添加A类及其子类B
        l1.add(new A());
        l1.add(new B());
        //报错,但不能添加除上的其他对象
        //l1.add(new Object());
    }
相关文章
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
91 43
Java学习十六—掌握注解:让编程更简单
|
22天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
2月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
36 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
26 0
[Java]泛型
|
2月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
15 1
|
2月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
49 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
2月前
|
前端开发 Java 应用服务中间件
Javaweb学习
【10月更文挑战第1天】Javaweb学习
33 2
|
2月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
19 1
|
2月前
|
存储 算法 Java
带你学习java的数组军队列
带你学习java的数组军队列
35 0