Java学习笔记 13、泛型

简介: Java学习笔记 13、泛型

一、认识泛型


首先提出一个问题:为什么有泛型?


设计背景:集合容器在设计阶段/声明阶段不能确定该容器实际存的是什么类型的对象,所以在JDK1.5之前统一将容器中的元素设计为Object,在JDK1.5之后使用泛型来解决。

将元素的类型设计成一个参数,这个参数就叫做泛型。例如:interface Collection<E> extends Iterable<E>其中的E就是泛型,根据传入类型来定。

泛型:就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时确定(例如继承实现某某接口,声明变量,创建对象)。


一个类继承了父类,那么就能通过它的父类找到对应的子类,但是不能通过其他类来找到具体要找的和这个类。

好处:使用泛型能够规范集合中的元素,避免了强转出现异常的情况,没有实现泛型前是类型不安全的。


重要点:JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持。若是不指定类型也可以默认是Object。



JDK1.7新特性:类型推断


//当我们创建实例时,new后面不需要设置指定类型了(前面一定要有)
Map<String,Integer> map = new HashMap<>();



二、自定义泛型结构


自定义泛型举例


class MyGeneric<T>{
    private String name;
    private T Order;
    public void setOrder(T order) {
        Order = order;
    }
    public T getOrder() {
        return Order;
    }
}


这里T作为泛型,等待创建实例传入指定的类型参数,并且该类属性也能够使用该泛型。


继承泛型类两种方式

方式一:不属于泛型类,直接确定类型


//当子类不使用泛型时,其继承的类需要明确类型
class Type1 extends MyGeneric<String>{//这里使用的上面自定义例子的类
}


方式二:属于泛型类,根据传过来的类型决定


//可以通过创建Type2实例时将类型也传递给MyGeneric
class Type2<T> extends MyGeneric<T>{
}



自定义泛型注意点

1、泛型类可以有多个参数,多个参数要一起放在<>中,例如<T,E,K>。


2、泛型类的构造器不需要使用<E>,和往常一样即可。


3、使用泛型类若是不指定类型,对应类型默认为Object。


4、若泛型类是一个接口或抽象类,不能创建泛型类的对象。


5、泛型指定只能使用引用数据类型,若是想要表示基本数据类型可用包装类代替。


6、异常类不能指定泛型。



三、泛型不同使用情境


泛型表示接口
interface MyGeneric<E>{
    E get();
}


一般泛型接口常用于生成器,即对象工厂,一种专门用来创建类的工厂对象。



泛型方法

泛型方法:在方法中出现泛型的结构,泛型参与类的泛型参数没有任何关系。


情况一:使用类中的泛型


class MyGeneric<T>{
    private String name;
    private T Order;
    //这里的T与创建实例时传入的类型一致
    public List<T> getList(List<T> arr){
        return arr;
    }
}


情况二:若是单独在方法中使用泛型,则需要在返回参数之前加上类似于<E>标记


class MyGeneric<T>{
    private String name;
    private T Order;
    //为了让E不表示一个类而是一个参数则需要在返回参数前加<E>
    public <E> List<E> copy(List<E> arr){
        return arr;
    }
}


不加标记会将E识别为一个类,从而找不到该类出现编译错误:




四、泛型继承上的体现


①类A是类B的超类,G<A> 和 G<B> 二者不具备子父类关系,二者是并列关系



②类A是类B的父类,A<G> 是 B<G> 的父类



总结:对于具有子父类关系的泛型类,其泛型应该一致,否则不具备子父类关系。



五、通配符使用


通配符为:?


通过使用?通配符能够解决之前G<A> 和 G<B>的问题,这两者共同的父类为G<?>



运用到方法中:


import java.util.*;
public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        traversal(list);
        traversal(list2);
    }
    //可遍历任意list<A> List<B>
    public static void traversal(List<?> list){
        Iterator<?> iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        //或者
//        for (Object o : list) {
//            System.out.println(o);
//        }
    }
}



注意点:例如上面List使用?不能进行add()的添加操作,获取值也只能获取到Object类型。


@Test


public void test(){
    List<String> list1 = new ArrayList<>();
    //给list1添加两个String字符串
    list1.add("AA");
    list1.add("BB");
    List<?> list2 = null;
    list2 = list1;
    //添加(写入):对于List<?> 就不能向其内部添加数据
    //除了添加null之外
    //        list2.add("AA");
    //        list2.add('?');//两个都会进行报错
    list2.add(null);//添加null值是允许的
    //获取读取:允许读取数据,读取的数据类型为Object
    Object o = list2.get(0);
    System.out.println(o);//AA
}



有限制条件的通配符使用


<? extends Number>:只允许泛型为Number及Number子列的引用调用。


<? super Number>:只允许泛型为Number及Number父类的引用调用。


extends:使用时只能使用对应类及其子类。

super:使用时只能使用对应类及其父类。

例子如下:


public static void main(String[] args) {
    List<? extends Person> list = new ArrayList<>();
    List<? super Person> list2 = new ArrayList<>();
    List<Person> list3 = new ArrayList<>();
    List<Student> list4 = new ArrayList<>();
    List<Object> list5 = new ArrayList<>();
    //赋值操作
    list = list3;
    list = list4;
    list = list5;//无法赋值,Object是Person父类
    list2 = list3;
    list2 = list4;//无法赋值,Student是Person子类
    list2 = list5;
    //获取对应值
    Person person = list.get(0);//由于泛型范围为Person及其子类,所以返回一个最大父类Person
    Object object = list2.get(0);//由于泛型范围为Person及其父类,所以返回一个最大父为Object
}



注意其中引用赋值范围以及获取对应泛型参数值的范围!!!


六、泛型中E、T、K等含义


其实可以使用任何合法的Java标识字符串,习惯用法是单一的字母表示,为了好辨识某些泛型的含义,特使用如下几个字母:


E:可表示Element,一般在集合中使用,集合中存放元素。

T:可表示Type,Java类。

K:可表示Key,作为键,Map中使用到。

V:可表示Value,作为值,Map中使用到。

N:Number,数值类型。

?:表示不确定的java类型。


相关文章
|
2月前
|
Java API
[Java]泛型
本文详细介绍了Java泛型的相关概念和使用方法,包括类型判断、继承泛型类或实现泛型接口、泛型通配符、泛型方法、泛型上下边界、静态方法中使用泛型等内容。作者通过多个示例和测试代码,深入浅出地解释了泛型的原理和应用场景,帮助读者更好地理解和掌握Java泛型的使用技巧。文章还探讨了一些常见的疑惑和误区,如泛型擦除和基本数据类型数组的使用限制。最后,作者强调了泛型在实际开发中的重要性和应用价值。
41 0
[Java]泛型
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
45 0
|
2月前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
18 1
|
3月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
44 9
Java——包装类和泛型
|
2月前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
27 5
|
2月前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
19 1
|
2月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
22 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
3月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。