java 泛型

泛型:

泛型是一种不确定的类型,需要使用的时候根据传进来的数据类型来确定

因为java是强类型的语言,当我们不确定数据类型的时候,最好用泛型

public class Point<T>{
	T x;
	T y;
}

注意,T是泛型参数,表示一种数据类型,具体是什么类型,需要将来使用Point的时候进行传参来确定

注意,如果将来Point在使用的时候,没有给泛型参数T传值,那么T默认就表示为Object类型

注意,T是泛型参数的名字,也就是相当于形参,名字随便起,但是一般用一个有意义的大写字母

Point 类的使用:

public static void main(String[] args) {
    //p1对象中x和y的属性类型都是Integer
    Point<Integer> p1 = new Point<Integer>();
    p1.x = 1;
    p1.y = 2;
    //p2对象中x和y的属性类型都是String
    Point<String> p2 = new Point<String>();
    p2.x = "1";
    p2.y = "2";
    //p3对象中x和y的属性类型都是Object
    Point p3 = new Point();
    p3.x = new Object();
    p3.y = new Object();
}

Point类的中x和y属性的类型,是可以根据我们在使用时所传的参数,进行临时变化的
如果没有传这个泛型参数,那么这个参数T就默认是Object类型
注意,给泛型参照传的值,只能是引用类型,不能是基本类型: Point 编译报错

泛型也叫做 类型参数化(Parameterized by types)

大写字母一般使用
T type 类型

E element 元素

K key

V value

方法重载时,泛型不同重载不算成功

public void Test(List<String>){

}
public void Test(List<Integer>){
    
}//由于类型擦除,jvm编译的时候默认两个方法一样

泛型的种类

java中的泛型分三种使用情况:
泛型类
泛型接口
泛型方法

泛型类,如果泛型参数定义在类上面,那么这个类就是一个泛型类
在类中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定

public class Point<T>{...}

	public static void main(String[] args){
		Point<String> p = new Point<>();
	}

泛型接口,如果泛型参数定义在接口上面,那么这个接口就是一个泛型接口
在接口中,就可以使用这个T来代表某一个类型,这个类型具体是什么将来使用的时候再传参确定

public interface Action<T>{...}

public static void main(String[] args){
    //创建匿名内部类
    Action<String> a = new Action<>(){
    //...
	};
}

泛型方法,如果泛型参数定义在方法上面,那么这个方法就是一个泛型方法

public class Test{
    public <T> T test(T t){
    //..
    }
}

public static void main(String[] args){
    Test t = new Test();
    String str = t.test("hello");
    Integer i = t.test(1);
    Double d = t.test(10.5D);
}

可以看出,调用test方法时候,我们传什么类型的参数,test方法的返回类型就是什么类型的。

通配符

先观察如下情况

public void test1(Collection<Integer> c){
}
public void test2(Collection<String> c){
}
public void test3(Collection<Object> c){
}

在这种情况下,就可以使用通配符(?)来表示泛型的父类型:

public void test(Collection<?> c){
    
}

这时候test方法中的参数类型,使用了泛型,并且使用问号来表示这个泛型的类型,这个问号就是通配符,可以匹配所有的泛型类型

test方法可以接收 泛型是任意类型的 Collection集合对象

Collection<?> c = new ArrayList<String>();
//编译报错
//因为变量c所声明的类型是Collection,同时泛型类型是通配符(?)
//那么编译器也不知道这个?将来会是什么类型,因为这个?只是一个通配符
//所以,编译器不允许使用变量c来向集合中添加新数据。
c.add("hello");
//编译通过
//但是有一个值是可以添加到集合中的,null
//集合中一定存的是引用类型,null是所有引用类型共同的一个值,所以一定可以添加进去。
c.add(null);

//虽然使用通配符(?)的集合,不能再往其中添加数据了,但是可以遍历集合取出数据:
public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("hello1");
    list.add("hello2");
    list.add("hello3");
    list.add("hello4");
    
    Collection<?> c = list;
    //编译报错
    //c.add("hello5");
    for(Object obj : c){
    	System.out.println(obj);
    }
}


//所有?通配符一般在传参的时候用

泛型边界

在默认情况下,泛型的类型是可以任意设置的,只要是引用类型就可以。
如果在泛型中使用 extends 和 super 关键字,就可以对泛型的类型进行限制。即:规定泛型的上限和下限。

泛型的上限:
例如: List<? extends Number> list
将来引用list就可以接收泛型是 Number 或者 Number 子类型的List集合对象

public static void main(String[] args) {
    List<? extends Number> list;
    //list可以指向泛型是Number或者Number【子】类型的集合对象
    list = new ArrayList<Number>();
    list = new ArrayList<Integer>();
    list = new ArrayList<Double>();
    //编译报错,因为String不是Number类型,也不是Number的子类型
    //list = new ArrayList<String>();
}

能表示数字的类型都是Number类型的子类型,例如Byte Short Integer Long等

泛型的下限:
例如: List<? super Number> list
将来引用list就可以接收泛型是 Number 或者 Number 父类型的List集合对象

public static void main(String[] args) {
    List<? super Number> list;
    //list可以指向泛型是Number或者Number【父】类型的集合对象
    list = new ArrayList<Number>();
    list = new ArrayList<Serializable>();
    list = new ArrayList<Object>();
    //编译报错,因为String不是Number类型,也不是Number的父类型
    //list = new ArrayList<String>();
    //编译报错,因为Integer不是Number类型,也不是Number的父类型
    //list = new ArrayList<Integer>();
}

泛型中 extends 和 super 对比:
使用extends可以定义泛型的【上限】,这个就表示将来泛型所接收的类型【最大】是什么类型。
可以是这个最大类型或者它的【子类型】。
使用super可以定义泛型的【下限】,这个就表示将来泛型所接收的类型【最小】是什么类型。可
以是这个【最小类型】或者它的【父类型】。

extends 限定泛型的上限的使用场景

  1. 在声明泛型类或者泛型接口的时候【可以】使用
  2. 在声明泛型方法的时候【可以】使用
  3. 在声明变量的时候【可以】使用

super 限定泛型的下限的使用场景

  1. 在声明泛型类或者泛型接口的时候【不能】使用
  2. 在声明泛型方法的时候【不能】使用
  3. 在声明变量的时候【可以】使用

类型擦除

泛型类型仅存在于编译期间,编译后的字节码和运行时不包含泛型信息,所有的泛型类型映射到同一份 字节码。

注意,泛型信息被擦除后,所有的泛型类型都会统一变为原始类型:Object

例如,定义一个泛型类 Generic 是这样的:

class Generic<T> {
	private T obj;
	public Generic(T o) {
		obj = o;
	}
	public T getObj() {
		return obj;
	}
}

那么,Java编译后的字节码中 Generic 相当于这样的:(类型擦除,变为原始类型Object)

class Generic {
	private Object obj;
	public Generic(Object o) {
		obj = o;
	}
	public Object getObj() {
		return obj;
	}
}

Java的泛型只存在于编译时期,泛型使编译器可以在编译期间对类型进行检查以提高 类型安全,减少运行时由于对象类型不匹配引发的异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值