Java基础泛型

简介: 最近在回顾JavaSE部分的知识,对一些薄弱的知识进行记录,学习方式,通过视频和图书的进行学习,视频看B站韩顺平老师的30天学会Java,图书看Java核心技术 卷I 基础知识(原书第10版)。

个人推荐:

📢📢📢 前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下 "通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。 点击跳转到教程

在这里插入图片描述

前言

最近在回顾JavaSE部分的知识,对一些薄弱的知识进行记录,学习方式,通过视频和图书的进行学习,视频看B站韩顺平老师的30天学会Java,图书看Java核心技术 卷I 基础知识(原书第10版)。

韩顺平30天学会地址:Javahttps://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.999.0.0
在这里插入图片描述
Java核心技术 卷I 基础知识(原书第10版)
在这里插入图片描述

一.泛型的理解和好处

1.泛型的理解

(1) 使用传统方法的问题分析

创建三个类Cat,People,Test类,具体代码如下:

package com.dudu;

import java.util.ArrayList;
import java.util.List;


@SuppressWarnings({ "rawtypes", "unchecked" })
public class Test {
    public static void main(String[] args) {
        List list =new ArrayList();
        list.add(new Cat(1, "嘟嘟"));
        list.add(new Cat(2, "小白"));
        list.add(new Cat(3, "小花猫"));
        list.add(new People(4, "铲屎官"));
        
        for(Object olist:list){
            // 向下转型
            if (olist instanceof Cat) {
                Cat cat = (Cat)olist;
                System.out.println(cat.toString());    
            }else if (olist instanceof People) {
                People people =(People)olist;
                System.out.println(people.toString());    
            }
        }
    }

}


class Cat{
    private int id;
    private String name;
    public Cat(int id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Cat [id=" + id + ", name=" + name + "]";
    }
    
}

class People{
    private int id;
    private String name;
    public People(int id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override
    public String toString() {
        return "People [id=" + id + ", name=" + name + "]";
    }
    
}

运行效果:
在这里插入图片描述
上面的代码能跑,但是存在一些问题:
在遍历集合的时候,对于集合中的数据是通过判断实例类的方式执行不同向下转型,但是如果传入的是一个不确定的数据类型的时候,就无法获取该实例的属性方法,并且如果强制进行向下转型还会引发错误,所以这种方式既不安全,查询效率也低。

  • 不能对加入集合ArrayList中的数据类型进行约束(不安全)

在这里插入图片描述

  • 遍历的时候,需要进行类型转换,如果集合的数据较大,对效率有影响

在这里插入图片描述

(2) 使用泛型方式
//泛型基本语法:类名<T> 对象名 =new 类名/子类名<>(); // T为引用数据类型

使用泛型后,对集合中的数据进行限定后,不是该限定的数据类型,是无法加入到集合中去的,遍历的时候无需进行类型转换(编译的时候会自动识别该泛型的数据类型)。
在这里插入图片描述

2.泛型的好处

  • 编译时,检查添加元素的类型,提高了安全性
  • 减少了类型的转换次数,提高效率
  • 不再提示编译警告

二.泛型介绍

1.什么是泛型

传入的数据类型的数据类型,意思就是泛型是一种数据类型,这个数据类型需要编译的时候传入相应的数据类型,泛型可以限定类/接口的数据类型,可以在类/接口声明时通过一个标识 表示类/接口中某个属性的类型(接口不行) ,或者是 某个方法的返回值的类型,或者是 参数类型 ,是一种动态的数据类型。

代码:

package com.dudu;

public class Test2 {
    public static void main(String[] args) {
        GenericparaDigmpor<String,Integer,Boolean> genericparaDigmpor= new GenericparaDigmpor<>("张三",18,true);
        System.out.println(genericparaDigmpor.toString());
        
        GenericparaDigmpor<Integer,String,String> genericparaDigmpor2 =new GenericparaDigmpor<>(18,"张三","李四");
        System.out.println(genericparaDigmpor2.toString());
    }
}


class GenericparaDigmpor<T,M,G>{
    // 属性的数据类型
    T t;
    M m;
    G g;
    
    
    // 方法的返回数据类型
    public M getM() {
        return m;
    }
    
    // 参数的数据类型
    public void name(G g) {
        System.out.println(g.getClass().getSimpleName());
    }
    
    public GenericparaDigmpor(T t,M m, G g){
        this.t = t;
        this.m = m;
        this.g = g;
    }
    
    public GenericparaDigmpor() {
        // TODO Auto-generated constructor stub
    }

    @Override
    public String toString() {
        return "GenericparaDigmpor [t=" + t.getClass().getSimpleName() + 
                ", m=" + m.getClass().getSimpleName() + ","
                + " g=" + g.getClass().getSimpleName() + "]";
    }
    
    
}

运行效果:
在这里插入图片描述

2.泛型的语法

  • 泛型的声明

interface 接口<T>{}class 类<K,T,V,...>{}

说明:
(1)其中,T.K,V不代表值,而是表示类型(泛型标识符)。
(2)任意字母都可以(一般为大写字母)。常用T表示,是Type的缩写。
(3)....表示泛型标识符可以有多个

  • 泛型的实例化

类名<泛型的数据类型> 对象名 = new 类名/子类名<泛型的数据类型>();

List<String> strList = new ArrayList<String>();

3.泛型使用细节

  • 泛型的使用形式(JavaSE7及其以后版本,可以使用省略写法)

类名<泛型的数据类型> 对象名 = new 类名/子类名<>();

List<String> strList = new ArrayList<>();

  • 有泛型限定的类中(如集合类),不加任何泛型修饰,默认使用数据类型为Object的泛型

List list =new ArrayList(); 相当于 List<object> list =new ArrayList<>();

未使用泛型:
在这里插入图片描述
使用泛型:
在这里插入图片描述
注意:
List<Object> list = new ArrayList<>(); 不要写成 List list = new ArrayList(); 这种形式。

  • 泛型的数据类型是引用数据类型,对于一些基本数据类型,需要装箱为包装类

当泛型的数据类型为基本数据类型时,会报错!
在这里插入图片描述
在这里插入图片描述
虽然传入的是int但是实际上会装箱为Integer
在这里插入图片描述
上面的代码类似于:
在这里插入图片描述

  • 在指定泛型具体类型后,可以传入该数据或者其子类类型(多态)
import java.util.List;

public class Test3 {
            public static void main(String[] args) {
                List<Animal> list =new ArrayList<>();
                // 传入该数据
                list.add(new Animal());
                // 传入该数据的子类对象
                list.add(new Cat_animal());
                // 传入该数据的子类对象的子类对象
                list.add(new DuDu());
                //list.add(new Alien());
            }
}


class Animal{
    public Animal() {
        // TODO Auto-generated constructor stub
        System.out.println(this.getClass().getSimpleName());
    }
}


class Cat_animal extends Animal{
    
    
}
class DuDu extends Cat_animal{
    
}


class Alien {
    
}

运行效果:
在这里插入图片描述

三.自定义泛型

1.自定义泛型类

自定义泛型类就是自己定义的一个带泛型的类,在传统类名后面加上<T,R,...>

语法:

class 类名<T,R...>{
    // 成员
}

注意细节:

  • 普通成员(属性,方法)可以使用泛型(前面已经说了)
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型
  • 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型) (前面已经说了)
  • 如果在创建对象时,没有指定类型默认为Object(前面已经说了)

使用泛型的数组,不能初始化:
在这里插入图片描述

静态方法,属性中不能使用类中的泛型标识符(静态在类加载的时候加载,执行在对象的前面,泛型是在创建对象的时候数据类型才确定,所以在静态中无法使用):
在这里插入图片描述
在这里插入图片描述

2.自定义泛型接口

自定义泛型接口就是自己定义的一个带泛型的接口,在传统接口名后面加上<T,R,...>

语法:

interface 接口名<T,R...>{
}

代码:

package com.dudu;

public class Test5 {
                public static void main(String[] args) {
                    TigerSum tigerSum = new TigerSumImple();
                    tigerSum.method1("你好");
                    tigerSum.method3(2.6);
                }
}


// 泛型接口
interface Tiger_Two<T,M,G> {
    void method1(T t);
    M method2();
    G method3(G g);
}

interface TigerSum extends Tiger_Two<String, String, Double>{
    
}

class TigerSumImple implements TigerSum{

    @Override
    public void method1(String t) {
        // TODO Auto-generated method stub
        System.out.println(t.getClass().getSimpleName());
    }

    @Override
    public String method2() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Double method3(Double g) {
        // TODO Auto-generated method stub
        System.out.println(g.getClass().getSimpleName());
        return null;
    }
    
}

运行效果:
在这里插入图片描述

注意细节:

  • 接口中,静态成员(属性)也不能使用泛型(这个和泛型类规定一样)
  • 泛型接口的类型,在继承接口或者实现接口时确定
  • 没有指定类型,默认为Object

接口中的属性为静态属性,不能使用泛型
在这里插入图片描述
泛型接口的类型,在实现接口时确定:
在这里插入图片描述
泛型接口的类型,在继承接口时确定:
在这里插入图片描述

泛型接口默认泛型类型为Object:
在这里插入图片描述

3.自定义泛型方法

语法:

修饰符 <T,R..> 返回类型 方法名(参数列表){

}

注意: 一般泛型方法的参数列表都会有该泛型方法的泛型数据类型,不然泛型方法的数据类型采用的是默认数据类型Object。

代码:

package com.dudu;

public class Test6 {
                public static void main(String[] args) {
                    XiaoHu<String> xiaoHu =new XiaoHu<>();
                    xiaoHu.method1();
                    xiaoHu.method2("你好");
                    xiaoHu.method2(new XiaoHu<String>());
                    
                    xiaoHu.method3("你好");
                    xiaoHu.method3("Hello World!");
                }
}

// 泛型类中可以使用泛型方法
class XiaoHu<G>{

    // 这是一个泛型方法,但是未使用该泛型参数(一般会使用)
    public <K> void method1() {
        System.out.println("这是一个泛型方法");
    }
    
    public <T> void method2(T t) {
        System.out.println("这是一个泛型方法,传入的泛型数据类型为:"+t.getClass().getSimpleName());
    }
    
    public void method3(G g) {
        System.out.println("这不是泛型方法,传入的泛型数据类型为:"+g.getClass().getSimpleName());
    }
}

// 接口中可以使用泛型方法
interface XiaoHong{
    <T> void method1();
    <G> void method2(G g);
}

// 普通类中可以使用泛型方法
class XiaoMing{
    public <G> void method1() {
        System.out.println("这是一个泛型方法");
    }
    
    public <T> void method2(T t) {
        System.out.println("这是一个泛型方法,传入的泛型数据类型为:"+t.getClass().getSimpleName());
    }
    
}

运行效果:
在这里插入图片描述

注意细节:

  • 泛型方法,可以定义在普通类中,也可以定义在泛型类/接口中
  • 当泛型方法被调用时,类型会确定
  • 参数为泛型的方法不一定是泛型方法(方法的泛型标识符可能来自于类上的标识)

四.通配符

1.泛型继承性

泛型不具有继承性,但可以接受具有继承关系类的值

ArrayList后面的 <> 要么不填(使用省略写法),要么就要填入和前面一致的数据类型!
在这里插入图片描述
泛型可以接受具有继承关系类的值(多态)
在这里插入图片描述

2.通配符

  • <?> 支持任意泛型类型
  • <? extends A>:支持A类及其A类的子类,规定了泛型的上限(A类对象)
  • <? super A>:支持A类及其A类的父类,不限于直接父类,规定了泛型的下限(A类对象)

在这里插入图片描述
代码:

package com.dudu;

import java.util.ArrayList;
import java.util.List;

public class Test8 {
            public static void main(String[] args) {
                M m =new M();
                m.method1(new ArrayList<String>());
                
                
                m.method2(new ArrayList<A>());
                m.method2(new ArrayList<A_Son>());
                //m.method2(new ArrayList<A_Super>());
                
                m.method3(new ArrayList<A>());
                //m.method3(new ArrayList<A_Son>());
                m.method3(new ArrayList<A_Super>());
            }
}

class M{
    public void method1(List<?> list) {
        System.out.println("支持任意泛型类型");
    }

    public void method2(List<? extends A> list) {
        System.out.println("支持A类及其A类的子类");
    }
    
    public void method3(List<? super A> list) {
        System.out.println("支持A类及其A类的父类");
    }
}


class A_Super{
    public A_Super(){
        System.out.println(this.getClass().getSimpleName());
    }
    
}

class A extends A_Super{
    public A(){
        System.out.println(this.getClass().getSimpleName());
    }
}

class A_Son extends A{
     public A_Son(){
         System.out.println(this.getClass().getSimpleName());
     }
}




运行效果:
在这里插入图片描述

如果要传入一个带泛型的集合对象的时候 List<T> 无法直接作为参数的数据类型
在这里插入图片描述

此时就可以使用 List<?> :
在这里插入图片描述

在这里插入图片描述

并且我们还可以对 <?> 中的 ? 使用 <? extends A><? super A> 进行限定。
如:
<? extends A> :支持A类及其A类的子类; <? super A> :支持A类及其A类的父类:

在这里插入图片描述

相关文章
|
2月前
|
安全 Java 编译器
揭秘JAVA深渊:那些让你头大的最晦涩知识点,从泛型迷思到并发陷阱,你敢挑战吗?
【8月更文挑战第22天】Java中的难点常隐藏在其高级特性中,如泛型与类型擦除、并发编程中的内存可见性及指令重排,以及反射与动态代理等。这些特性虽强大却也晦涩,要求开发者深入理解JVM运作机制及计算机底层细节。例如,泛型在编译时检查类型以增强安全性,但在运行时因类型擦除而丢失类型信息,可能导致类型安全问题。并发编程中,内存可见性和指令重排对同步机制提出更高要求,不当处理会导致数据不一致。反射与动态代理虽提供运行时行为定制能力,但也增加了复杂度和性能开销。掌握这些知识需深厚的技术底蕴和实践经验。
66 2
|
10天前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
41 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
5天前
|
存储 安全 Java
🌱Java零基础 - 泛型详解
【10月更文挑战第7天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
6 1
|
13天前
|
Java 语音技术 容器
java数据结构泛型
java数据结构泛型
23 5
|
10天前
|
存储 Java 编译器
Java集合定义其泛型
Java集合定义其泛型
14 1
|
13天前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
16 2
|
1月前
|
Java 编译器 容器
Java——包装类和泛型
包装类是Java中一种特殊类,用于将基本数据类型(如 `int`、`double`、`char` 等)封装成对象。这样做可以利用对象的特性和方法。Java 提供了八种基本数据类型的包装类:`Integer` (`int`)、`Double` (`double`)、`Byte` (`byte`)、`Short` (`short`)、`Long` (`long`)、`Float` (`float`)、`Character` (`char`) 和 `Boolean` (`boolean`)。包装类可以通过 `valueOf()` 方法或自动装箱/拆箱机制创建。
30 9
Java——包装类和泛型
|
1月前
|
安全 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版)
|
10天前
|
安全 Java 编译器
Java基础-泛型机制
Java基础-泛型机制
11 0
|
1月前
|
存储 安全 搜索推荐
Java中的泛型
【9月更文挑战第15天】在 Java 中,泛型是一种编译时类型检查机制,通过使用类型参数提升代码的安全性和重用性。其主要作用包括类型安全,避免运行时类型转换错误,以及代码重用,允许编写通用逻辑。泛型通过尖括号 `&lt;&gt;` 定义类型参数,并支持上界和下界限定,以及无界和有界通配符。使用泛型需注意类型擦除、无法创建泛型数组及基本数据类型的限制。泛型显著提高了代码的安全性和灵活性。