Java基础泛型

前言

最近在回顾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类的父类:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42753193/article/details/123890650