java基础-泛型

1.简单理解泛型的作用


下面看一段代码

List list = new ArrayList();
		list.add(123);
		list.add("string");
		
		String s1 = (String) list.get(0); //易出错
		for(int i = 0 ; i < list.size() ; i++) {
			System.out.println(list.get(i));
		}

虽然List是add()一个Object,你可以传入 int ,String 等,但是你get()的时候也是返回一个Object。如果你想要转换成自己的类并操作,就要向上图一样实现强制转换,虽然代码没错,但是一运行就出错。有时候很多类型,你可能已经忘记要转换成什么类型,那是不是会出错呢。

比如上面就会报错:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

所以就要想一个办法,让List记住你add()传入的参数类型,当你get()的时候不需要强制转换,它自动已经帮你转换好了。减少错误的发生。例如:

List<String> list2 = new ArrayList<String>();
		list2.add("qweq");
		//list2.add(456); //报错
		
		String s = list2.get(0);
		for(int i = 0 ; i < list2.size() ; i++) {
			System.out.println(list2.get(i));
		}

代码中 <String> 就是泛型.虽然减少了错误发生,但是也对你add()加了限制,不再是传入Object,而是String。这样你get()的也是String。

2.泛型概念


泛型是Java SE 1.5的新特性,本质是参数化类型,所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

3.我们来自己定义个 泛型类 和 泛型方法 (泛型接口类似)


在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E 等形式的参数常用于表示泛型形参,接收来自外部使用时候传入的类型实参。

注意:泛型的类型参数只能是类类型(包括自定义类),不能是简单类型如(int)。

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

		Person<String> p1 = new Person<String>("2018");
		System.out.println(p1.getId());
		
		//Person<int> p2 = new Person<int>(8102); //不能是int,可以是Integer
		Person<Integer> p2 = new Person<Integer>(123);
		System.out.println(p2.getId());
		
	}
}

class Person<T>{
	private T id;
	public Person(T id) {
		this.id = id;
	}
	public T getId() {
		return id;
	}
}

使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为Person)。

究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

System.out.println(p1.getClass() == p2.getClass()); //true

4.类型通配符


先讲个例子,理解为什么需要类型通配符?

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

		Person<String> p1 = new Person<String>("2018");
		System.out.println(p1.getId());
		
		//Person<int> p2 = new Person<int>(8102); //不能是int,可以使Integer
		Person<Integer> p2 = new Person<Integer>(123);
		System.out.println(p2.getId());
		
		System.out.println(p1.getClass() == p2.getClass()); //true
		
		//getId(p1); //报错 ←
		//getId(p2); //报错 ←
		
		
	}
	public static void getId(Person<Object> p) { //泛型参数方法
		System.out.println(p.getId());
	}
}

class Person<T>{
	private T id;
	public Person(T id) {
		this.id = id;
	}
	public T getId() {
		return id;
	}
}

可以看到上面代码中有2个报错。原因如下

Person<Object> 不能看做是 Person<String> / Person<Integer> 的父类。可以推导其它的子父类也不成立。

那么我们想要让Person<String> / Person<Integer>传入方法中该怎么办?    [ Person<?> p ]

这时候就需要我们的  类型通配符(?)  修改代码如下:

public static void main(String[] args) {

		Person<String> p1 = new Person<String>("2018");
		System.out.println(p1.getId());
		
		//Person<int> p2 = new Person<int>(8102); //不能是int,可以使Integer
		Person<Integer> p2 = new Person<Integer>(123);
		System.out.println(p2.getId());
		
		System.out.println(p1.getClass() == p2.getClass()); //true
		
		getId(p1); // ←
		getId(p2); // ←
		
		
	}
	public static void getId(Person<?> p) {
		System.out.println(p.getId());
	}

我们可以看到错误没有了。

虽然错误没有了,但是是不是觉得参数范围很广,我们如果想要缩小参数范围又该怎么办?   (类型通配符上限和类型通配符下限)

  • 类型通配符上限通过形如Person<? extends Number>形式定义       Number为上限

(类型通配符上限: 只想让方法的参数为某个类和它的子类) 

  • 相对应的,类型通配符下限为Box<? super Number>形式                Number为下限

(类型通配符下限: 只想让方法的参数为某个类和它的父类)

代码如下:

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

		Person<String> p1 = new Person<String>("2018");
		System.out.println(p1.getId());
		
		//Person<int> p2 = new Person<int>(8102); //不能是int,可以使Integer
		Person<Integer> p2 = new Person<Integer>(123);
		System.out.println(p2.getId());

		Person<Number> p3 = new Person<Number>(456);
		System.out.println(p3.getId());
		
		Person<Object> p4 = new Person<Object>("爱你");
		
		//getId_extends(p1); // 报错,String不是Number的子类        ←
		getId_extends(p2);   // 不报错,Integer是Number的子类    ←
		getId_extends(p3);   // 本身肯定不报错                ←
		//getId_extends(p4); // 报错,Object不是Number的子类      ←
		
		//getId_super(p1); // 报错,String不是Number的父类         ←
		//getId_super(p2); // 报错,Integer不是Number的父类      ←
		getId_super(p3);   // 本身肯定不报错                 ←
		getId_super(p4);   // 不报错,Object是Number的父类         ←
		
		
	}
	public static void getId_extends(Person<? extends Number> p) {
		System.out.println(p.getId());
	}
	public static void getId_super(Person<? super Number> p) {
		System.out.println(p.getId());
	}
}

5.泛型数组


一直听说没有泛型数组这个东西,可能是我学的还比较浅吧。

不过我自己还是实验出来可以用的,如下:

Person<?>[] ps = new Person<?>[4];
        ps[0] = p1;
        ps[1] = p2;
        ps[2] = p3;
        ps[3] = p4;
        for(int i = 0 ; i < ps.length ; i++) {
            System.out.println(ps[i].getId());
        }

不过,若是你的泛型参数传入的不是 通配符(?),而是其它的比如(Integer , String , Object)就不行。看来实现泛型数组,必须要

通配符<?>。

猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/82831583