泛型「generic」讲解

news2025/7/29 1:45:44

标题

  • 1. 泛型语法
    • 1.1 泛型的引出
    • 1.2 泛型介绍
    • 1.3 泛型语法
    • 1.4 泛型使用细节
    • 1.5 练习题
  • 2.自定义泛型
    • 2.1自定义泛型-类
    • 2.2自定义泛型-接口
    • 2.3自定义泛型-方法
    • 2.4练习题
  • 3.范型继承和通配符
    • 3.1JUnit单元测试框架
    • 3.2练习题

1. 泛型语法

1.1 泛型的引出

  1. 传统方法不能对加入到集合中的数据类型进行约束;
  2. 对集合遍历的时候需要进行类型转换,效率低;
public class Generic01 {
	public static void main(String[] args)ArrayList arrayList = new ArrayList();
		arrayList.add(new Dog("scott",4));
		arrayList.add(new Dog("jack",5));
		arrayList.add(new Dog("mary",6));
		for(Object o : arrayList) {
			Dog dog = (Dog) o;//获取对象的名字
			System.out.println(dog.getName());
		}}
class Dog {
	private String name;
	private int age;
	public Dog(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getAge() {
		return age;
	}
}
  1. 引出:new ArrayList<Dog>(),表示存放到ArrayList 集合中的元素是Dog类型
  2. 优点
    • 如果编译器发现添加的类型不满足要求,就会报错,即编译时编译器会检查添加元素的类型,提高安全性;
    • 遍历时,可以直接取出Dog类型,而不是Object类型,减少了类型转换的次数;
public class Generic01 {
	public static void main(String[] args)ArrayList arrayList = new ArrayList<Dog>();
		arrayList.add(new Dog("scott",4));
		arrayList.add(new Dog("jack",5));
		arrayList.add(new Dog("mary",6));
		//arrayList.add(new Cat("招财猫",3));
		for(Dog dog : arrayList) {
			System.out.println(dog.getName());
		}
	}
}

不使用泛型:放入到ArrayList会先转成Object,在取出时,再转换成Dog;
使用泛型:放入和取出时,不需要类型转换;

不使用泛型使用泛型
放入到ArrayList会先转成Object,在取出时,再转换成Dog;放入和取出时,不需要类型转换;

1.2 泛型介绍

  1. 泛型是可以表示数据类型的一种数据类型;
  2. 泛型又称为参数化类型,是jdk5.0出现的新特性,可以解决数据类型的安全性问题;
  3. 在类声明或者实例化的时候只需要指定需要的具体类型即可;
  4. Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException的异常;同时,代码更简洁、健壮。
  5. public class ArrayList<E> ,相当于把Dog赋给了E,Dog->E
  6. 泛型的作用是: 可以在类声明时通过一个标识符表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
    - 参数的类型
    - 类中方法返回值的类型
    - 属性的类型
泛型Generic
参数的类型$1600
类中方法返回值的类型$12
属性的类型$1
  1. 在这里,E的类型一定要清楚的知道,可以通过getClass()方法得到;
  2. 注意:E具体的数据类型在定义Person对象时指定,即在编译期间就已确定类型;
    在这里插入图片描述

1.3 泛型语法

  1. interface 接口<T> {} 和 class 类<K,V>{}
  • 说明:
    • T、K、V不代表值,而是表示类型;
    • 任意字母都可以,常用T表示,T是Type的缩写
  1. 泛型的实例化:要在类名后面指定类型参数的值(类型),如:
    • (1)List<String> stringList = new ArrayList<\String>();
    • (2)Interator<\Dog> iterator = dog.iterator();
      在这里插入图片描述
      在这里插入图片描述

1.4 泛型使用细节

  1. interface List<T> {}, public class HashSet<E> {};
    T,E只能是引用类型,不能用基本数据类型;(Type argument cannot be of primitive type)
    在这里插入图片描述
  2. 在给泛型指定具体类型后,可以传入该类型或其子类类型;
    在这里插入图片描述
  3. 泛型的简写形式:省略构造器处的泛型表示,编译器会进行类型推断;
    List<Pig> list = new ArrayList<>();
  4. 如果没有指定泛型,默认泛型是Object;
    Map hashMap = new HashMap();
    等价于
    Map<Object, Object> hashMap = new HashMap<>();
    在这里插入图片描述
    在这里插入图片描述

1.5 练习题

在这里插入图片描述
改进后:封装后代码复用性更高;
在这里插入图片描述
在这里插入图片描述

2.自定义泛型

2.1自定义泛型-类

  1. 基本语法:class 类名<T,R…> {} 可以有多个泛型
  2. 普通成员可以使用泛型(属性、方法);
  3. 使用泛型的数组不能初始化;
  4. 静态成员中不能使用类的泛型;
  5. 泛型类的类型,是在创建对象时确定的;(因为在创建对象时,需要指定类型)
  6. 如果在创建对象时,没有指定类型,默认为Object;
public class CustomGeneric {
    public static void main(String[] args) {
        //T是Double,R是String,M是Integer
        Tiger<Double, String, Integer> tiger = new Tiger<>("john~~");
        tiger.setT(3.2);//OK
        System.out.println(tiger);
        Tiger tiger1 = new Tiger("john");
        //tiger.setT("yy");因为T是Object,"yy"是 Object子类,类型不对
        
    }
}
//1.Tiger后面有泛型,所以我们把Tiger 称为自定义泛型类
//2.T, R, M 泛型的标识符,一般是单个大写字母
//3.泛型的标识符可以有多个
//4.普通成员可以使用泛型 (属性、方法)
//5.使用泛型的数组,不能初始化
class Tiger<T, R, M> {
    String name;
    T t;//属性使用到泛型
    M m;
    R r;

    public Tiger(String name) {
        this.name = name;
    }
    //Type parameter 'T' cannot be instantiated directly
    //T[] ts = new T[8];类型不能确定,无法分配空间,因为数组在new的时候不能确定T的类型,就无法在内存开辟空间
    public Tiger(T t, M m, R r) {//构造器使用泛型
        this.t = t;
        this.m = m;
        this.r = r;
    }
    //因为静态是和类相关的,在类加载时,对象还没有创建
    //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
    /*static R r2;
    public static void m1(M m) {//cannot be referenced from a static context

    }*/

    //方法使用泛型

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public M getM() {
        return m;
    }

    public void setM(M m) {
        this.m = m;
    }

    public R getR() {//返回类型可以使用泛型
        return r;
    }

    public void setR(R r) {//方法使用到泛型
        this.r = r;
    }
}

2.2自定义泛型-接口

基本语法:interface 接口名 <T, R…> {}

  1. 接口中静态成员也不能使用泛型;(和泛型类规定一样)
  2. 泛型接口的类型,在继承接口或者实现接口时确定;
  3. 如果没有指定类型,默认为Object;(不规范,建议指定<Object>)
  4. 方法参数列表中的泛型直接给值,类定义时的泛型给类型;
/**
 * 泛型接口使用说明
 * 1.接口中,静态成员不能使用泛型
 * 2.泛型接口的类型是在继承接口或者实现接口时实现的
 */

//1.在继承接口时,指定泛型接口的类型
interface IA extends IUsb<String, Double> {

}
//当我们去实现IA接口时,因为IA在继承IUsb接口时,指定了U为String,R为Double
// 因此在实现IUsb接口方法时,使用String替换U,Double替换R
class AA implements IA {

    @Override
    public Double get(String s) {
        return null;
    }

    @Override
    public void hi(Double aDouble) {

    }

    @Override
    public void run(Double r1, Double r2, String u1, String u2) {

    }
}
//2.实现接口时,直接指定泛型接口的类型
//给U指定了Integer, 给R指定了Float
//所以当实现IUsb接口方法时,会使用Integer替换U,Float替换R
class BB implements IUsb<Integer, Float> {

    @Override
    public Float get(Integer integer) {
        return null;
    }

    @Override
    public void hi(Float aFloat) {

    }

    @Override
    public void run(Float r1, Float r2, Integer u1, Integer u2) {

    }
}

//3.没有指定泛型,就默认为Object
//等价于class CC implements IUsb<Object, Object> {}, 但是不规范
class CC implements IUsb {
    @Override
    public Object get(Object o) {
        return null;
    }

    @Override
    public void hi(Object o) {

    }

    @Override
    public void run(Object r1, Object r2, Object u1, Object u2) {

    }

}
interface IUsb<U, R> {//U=Integer, R=Float
    //接口中普通方法,可以使用泛型
    R get(U u);

    int n = 10;
    //U name = "3e";静态成员不能使用泛型
    void hi(R r);

    void run(R r1, R r2, U u1, U u2);

    //在jdk8以后,可以在接口中使用默认方法,在默认方法中可以使用泛型
    default R method(U u) {
        return null;
    }
}

2.3自定义泛型-方法

基本语法:修饰符[空格]<T, R> 返回类型 方法名(参数列表) {}

  1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中;
  2. 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
  3. public void ear(E e) {},修饰符后面没有 <T, R…>, 那么eat方法就不是泛型方法,只是使用了泛型;
  4. 泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型;
public class CustomMethodGeneric_ {
    public static void main(String[] args) {
        Car car = new Car();
        //2. 当泛型方法被调用时,泛型类型就会确定,否则调用不起来;
        car.fly("宝马",1200);//当调用方法时,传入参数,编译器就会确定类型
        System.out.println("===========");
        car.fly(2.2,1200);//当调用方法时,传入参数,编译器就会确定类型
        System.out.println("===========");
        //T:Sring, R:ArrayList
        Fish<String, ArrayList> fish = new Fish<>();
        //K:Float
        fish.hello(new ArrayList(), 2.1f);
    }
}
//1.泛型方法,可以定义在普通类中
class Car {//普通类
    public void run() {//普通方法

    }
    //说明:泛型方法
    // <T, R> 就是泛型
    // 提供给fly方法使用的
    public <T, R> void fly(T t, R r) {//泛型方法
        System.out.println(t.getClass());//String
        System.out.println(r.getClass());//Integer
    }
}
//2.泛型方法,也可以定义在泛型类中
class Fish<T, R> {//泛型类
    public void run() {//普通方法

    }

    public <U, M> void fly(U u, M m) {//泛型方法

    }
    //说明
    //  hi()方法不是一个泛型方法
    //  只是hi()方法使用了类声明的泛型
    public void hi(T e) {

    }
    //泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
    public <K> void hello(R r, K k) {
        System.out.println(r.getClass());//ArrayList
        System.out.println(k.getClass());//Float
    }
}

2.4练习题

public class CustomMethodGenericExercise {
    public static void main(String[] args) {
        //T:String, R:Integer, M:Double
        Apple<String, Integer, Double> apple = new Apple<>();
        apple.fly(10);//泛型都是引用类型,所以10自动装箱,输出:Integer
        apple.fly(new Dog());//输出:Dog
    }
}

class Apple<T, R, M> {
    public <E> void fly(E e) {
        //.getClass():显示包名+类名; 
        //.getClass().getSimpleName():只显示类名
        System.out.println(e.getClass().getSimpleName());
    }
    //public void eat(U u) {} //错误,U没有声明
    public <U> void eat(U u) {} //改正
    public void run(M m) {}

}
class Dog {}

3.范型继承和通配符

  1. 泛型不具备继承性;
    List<Object> list = new ArrayList<String> ();//这样写是错误的
  2. 通配符起到约束作用;
通配符作用
List<?> c表示可以接收任意范型类型
List<? extends A> c表示可以接收A类或者A类的子类,规定泛型上限
List<? super A> c表示可以接收A类或者A类的父类(不限于直接父类),规定泛型下限
@SuppressWarnings({"all"})
public class GenericExtends {
    public static void main(String[] args) {
        //范型没有继承性
        //List<Object> list = new ArrayList<String>();
        
        //举例说明下面三个方法的使用;
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();
        
        //1.如果是 List<?> c, 可以接收任意的范型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);
        
        //2.如果是 List<? extends AA> c, 可以接收AA或者AA的子类
        //printCollection2(list1);
        //printCollection2(list2);
        printCollection2(list3);
        printCollection2(list4);
        printCollection2(list5);

        //3.如果是 List<? super AA> c, 可以接收AA,AA的父类但不限于直接父类
        printCollection3(list1);//正确
        //printCollection3(list2);
        printCollection3(list3);
        //printCollection3(list4);
        //printCollection3(list5);
    }

    public static void printCollection1(List<?> c) {
        for (Object object : c) {//通配符,取出时就是Object
            System.out.println(object);
        }
    }

    public static void printCollection2(List<? extends AA> c) {
        for (AA object : c) {
            System.out.println(object);
        }
    }

    //List<?> 表示任意范型类型都可以接收
    //List<? extends AA> 表示的是上限,可以接收 AA或AA的子类
    //List<? super AA> 表示的是下线,可以接收 AA或者AA的父类,不限于直接父类
    public static void printCollection3(List<? super AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }
}
class AA {

}
class BB extends AA {

}
class CC extends BB {

}

3.1JUnit单元测试框架

shortcuts: Alt+Enter/option+Enter
可以直接运行一个方法,JUnit测试框架;
在这里插入图片描述

public class JUnit_ {
    public static void main(String[] args) {
        //new JUnit_().m1();
        //new JUnit_().m2();
    }
    @Test
    public void m1() {
        System.out.println("m1()方法");
    }
    @Test
    public void m2() {
        System.out.println("m2()方法");
    }
}

3.2练习题

public class Homework01 {
    public static void main(String[] args) {

    }

    @Test
    public void test() {
        //这里我们给泛型T指定的类型是User
        DAO<User> userDAO = new DAO<>();
        userDAO.save("No1", new User(1, 22));
        userDAO.save("No2", new User(2, 23));
        userDAO.save("No3", new User(3, 23));
        List<User> list = userDAO.list();
        userDAO.update("No3", new User(3, 18));
        list = userDAO.list();
        userDAO.delete("No3");
        list = userDAO.list();
        User user = userDAO.get("No2");
        System.out.println(list);
        System.out.println(user.getAge());
    }
}

class DAO<T> {
    private Map<String, T> map = new HashMap<>();

    public void save(String id, T entity) {
        map.put(id, entity);
    }

    public T get(String id) {
        return map.get(id);
    }

    public void update(String id, T entity) {
        map.put(id, entity);
    }

    public List<T> list() {
        List<T> arrayList = new ArrayList<>();
        Set<String> keySet = map.keySet();
        Iterator<String> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            arrayList.add(get(key));
        }
        return arrayList;
    }

    public void delete(String id) {
        map.remove(id);
    }
}
class User {
    private int id;
    private int age;

    public User(int id, int age) {
        this.id = id;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "\nUser{" +
                "id=" + id +
                ", age=" + age +
                '}';
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/16326.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker部署ELK

Docker部署ELK安装包下载1、安装docker2、安装docker compose3、使用docker加载离线镜像4、上传yml文件并初始化密码5、造数据&#xff0c;看采集结果安装包下载 链接:https://pan.baidu.com/s/1LOHyhTHm3-30v6wAfYLTAg 提取码:5uno 1、安装docker &#xff08;1&#xff09…

Python新手的福音,涵盖20+种初学者必练项目!

前言 对学Python的新手同学来说&#xff0c;最最最想要的干货是什么&#xff1f;是大量可以练手的项目啊。俗话说&#xff0c;三天不练手生&#xff0c;三天不念口生。#技术派的书架# &#xff08;文末送读者福利&#xff09; 在某乎上有一个关于"Python的练手项目”的问…

【线程】线程同步

目录 一、信号量 1.函数 2.使用 二、读写锁 1.函数 2.使用 三、互斥锁 1.函数 2.使用 四、条件变量 1.函数 2.使用 前言 线程同步的实现方法&#xff1a;信号量、互斥锁、条件变量、读写锁。 下面就对着四种方法进行展开描述 一、信号量 与进程间通信的信号量类似&a…

JNPF3.4.5大版本正式上线啦!

千呼万唤始出来&#xff0c;时隔近四个月&#xff0c;引迈信息终于再度推出新版本与大家见面了&#xff0c;此次推出的3.4.5大版本&#xff0c;可谓是吊足了大家的胃口。 本次大更新为用户带来的是高效率、高可用性、低成本、快速部署、易于扩展的快速开发平台的使用体验。本次…

【ASM】字节码操作 工具类与常用类 TraceClassVisitor 介绍

文章目录 1.概述2. TraceClassVisitor2.1 class info2.2 字段信息2.3 constructors2.4 methods3.如何使用TraceClassVisitor3.1 生成新的类3.2 修改已有的类3.3 打印ASM信息4.总结感谢第一个订阅字节码的人,感谢老铁支持 adminhjy 1.概述 在上一篇文章:

拓展卡尔曼滤波(Kalman)附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

分享5个良心好用的PC软件,免费无广告

今天再次推荐5个良心好用的Windows神级软件&#xff0c;每一个都是完全免费&#xff0c;堪称神器&#xff0c;让你打开新世界的大门。 1.数据恢复工具——EaseUS Data EaseUS Data Recovery Wizard是一款简单易用的数据恢复工具&#xff0c;给用户提供了三种恢复模式&#xf…

docker部署ES及kibana整个流程

对于ES小白&#xff0c;第一次安装ES走了很多弯路&#xff0c;下面记录自己本地安装elasticsearch的整个过程&#xff0c;我觉得小白如果按照我的流程走&#xff0c;大部分应该可以安装并运行成功。下面是整个步骤&#xff1a; 一、部署ES 拉取镜像 docker pull docker.elast…

胞外囊泡代谢组学—前列腺癌代谢变化研究的新策略

小小身体蕴含大大能量&#xff01;前列腺癌非侵入性的诊断和监测的新方式——胞外囊泡&#xff08;外泌体&#xff09;代谢组学&#xff01;目前&#xff0c;胞外囊泡/外泌体作为非侵入性的癌生物标志物已成为新的研究热点。百趣代谢组学文献分享&#xff0c;芬兰赫尔辛基大学学…

避免项目资源管理陷阱,8Manage帮你支招!

项目资源管理主要是对项目所需的人力、材料、机械、技术、资源等资源进行计划、组织、指挥、协调和控制。众所周知&#xff0c;项目推进需要资源支撑&#xff0c;一旦资源不足&#xff0c;项目的进度和质量都会受到影响。而在项目管理活动中&#xff0c;做好资源管理并不容易&a…

SORT追踪

卡尔曼滤波 卡尔曼滤波用当前时刻运动变量去预测下一时刻的运动变量&#xff0c;检测器第一次的检测结果用来初始化卡尔曼滤波的运动变量&#xff0c;后续的结果作为更新。在信号处理中卡尔曼滤波是去除噪声的一个算法&#xff0c;作用是使用信号更加的准确。在SORT中的&#x…

MySQL常用语句汇总

一、背景 日常测试开发工作中会用到各类SQL语句&#xff0c;很多时候都是想用的时候才发现语句细节记不清楚了&#xff0c;临时网上搜索SQL语法&#xff0c;挺费时费力的&#xff0c;语法还不一定是对的。因此汇总整理了一下MySQL最常用的各类语句&#xff0c;以后就不用再到处…

Linux环境安装

学习Linux首先要准备一个Linux环境。环境的安装有两种途径&#xff1a;买一个云服务器&#xff0c;安装虚拟机。 推荐使用云服务器&#xff0c;较虚拟机方便很多。 云服务器具体来说是Centos 7.6 64位——我也不知道为啥用这个 步骤一&#xff1a; 购买云服务器的主要方式…

“向美好女人致敬”粉红丝带主题活动,谈水果养生之道

传递粉红正能量&#xff0c;践行粉红关爱&#xff0c;“向美好女人致敬”粉红丝带关爱月公益线下活动于11月13日顺利收尾&#xff0c;帮助广大女性更加深入地了解、认识乳腺癌预防和康复治疗&#xff0c;推进乳腺癌防治意识。此次活动邀请到了云南省肿瘤医院乳腺三科科主任、副…

Android通过jni调用本地c/c++接口方法总结

网上有网友问android的原生应用,上层java代码如何通过jni调用本地的c/c++接口或第三方动态库 ?之前搞过android应用开发和底层c/c++接口开发都是一个人搞定,觉得还是蛮简单的。其实没啥难度,如果觉得难只是因为你没有经历过,只要搞过一遍基本就记住了。这里总结下方法留作…

李嘉诚人生最大的错误,并非错过阿里华为,而是套现中国投资欧洲

李嘉诚是很多人心中的生意之神&#xff0c;很多人认为李嘉诚一生从来都没有失败过&#xff0c;他是生意场的常胜将军。可是事实上真的是如此么&#xff1f; 很多人可能不知道&#xff0c;李嘉诚其实也曾经犯下了很大的错误&#xff0c;比如说2003年前后&#xff0c;李嘉诚先后错…

【设计模式】2.工厂模式

文章目录1. 工厂模式概述2. 简单工厂模式3. 工厂方法模式4. 抽象工厂模式1. 工厂模式概述 工厂模式属于创建型模式的一种。 在java中&#xff0c;万物皆对象&#xff0c;这些对象都需要创建&#xff0c;如果创建的时候直接new该对象&#xff0c;就会对该对象耦合严重&#xf…

图神经网络学习笔记

1 图神经网络应用 芯片设计、场景分析问题推理、推荐系统、欺诈检测风控相关、道路交通动态流量预测、自动驾驶、无人机等、化学医疗等场景 2 图神经网络基本组成 点(vertex)、边(edge)、全局图(global)&#xff0c;图神经网络&#xff08;GNN&#xff0c;Graph Neural Netw…

Java 集合知识点总结

Java 集合知识点总结总览Collection 接口ListArrayList源码&扩容机制SetQueueMap接口HashMapHashMap源码&底层数据结构HashMap 的遍历LinkedHashMapTreeMapHashtableConcurrentHashMap 源码&底层数据结构本文是个人阅读学习JavaGuide的集合知识的总结笔记。总览 C…

【计算机毕业设计】个人交友网站源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 本论文主要论述了如何使用JAVA语言开发一个个人交友网站&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0…