Java学习笔记之泛型(一)泛型类、泛型方法、泛型接口

为什么要引入泛型呢?
比如说有一个自己写的类Person,现在要创建一个工具类Tool来对其进行一些操作,代码如下:

class Person{
}

public class Tool {
	private Person person;

	public void setPerson(Person p){
    	this.person = p;
    }
    public Person getPerson(){
		return person;
	}
}

但是如果此时还有别的类比如说Person1、Person2等等,那就需要为以上每一个类都创建一个工具类Tool1、Tool2……,很麻烦不是吗?能不能用一个工具类来操作所有的这些类呢?想要操作所有类,那就需要找到这些类的共性,Java所有类都是继承自Object的吗?那改成Object不就行了嘛:

public class Tool {
	private Object object;

	public void setObject(Object object){
    	this.object = object;
    }
    public Person getObject(){
		return object;
	}
}

这种方法当然是可以的,但是也存在一些问题。比如说我们新建两个类Student和Worker都继承自Person:

class Student extends Person{
}
class Worker extends Person{
}

在主方法里面给这个工具类存一个Student:

public class Test {
	public static void main(String args[]) {
		Tool tool = new Tool();
		tool.setObject(new Student());
		Student student = (Student) tool.getObject();
	}
}

这时编译运行都可以通过,那么我们存一个Worker可不可以呢?

public class Test {
	public static void main(String args[]) {
		Tool tool = new Tool();
		tool.setObject(new Worker());
		Student student = (Student) tool.getObject();
	}
}

这时发现编译是可以通过的,但是运行会报错。为什么会这样呢?因为Tool里面接受的是Object类型的,也就是你不管存什么都莫的问题,但是取出来的时候Worker不能转型为Student所以报错。

为了提高程序的扩展性,便引入了泛型:

public class Tool<T> {
	private T t;

	public void setObject(T t){
    	this.t = t;
    }
    public Person getObject(){
		return t;
	}
}

可以发现,也就是用了大写字母T代替了Object。这时候在主方法里面:

public class Test {
	public static void main(String args[]) {
		Tool<Student> tool = new Tool<Student>();
		tool.setObject(new Student());
		Student student = tool.getObject();
	}
}

可以发现:这时候连强制类型转换都不用了,但是假如一不小心给它存了个Worker会怎样呢?
在这里插入图片描述
这时候不用等运行,首先编译就不通过了。好了,我们来总结一下泛型的好处:

  1. 不用强制类型转换
  2. 把运行时的问题转到了编译时期

可以发现,使用泛型要比Object要安全得多。刚刚的例子我们不知不觉就已经创建了一个泛型类Tool,那么问题来了,什么时候用泛型类呢?仔细想想就知道了,当类中的引用对象数据类型不确定时我们可以考虑使用泛型类。

好了,接下来再说说泛型方法。还是刚刚那个例子,我们给Tool添加一个方法:

public void show(T t){
	System.out.println("show" + t);
}

在主函数里面调用:
在这里插入图片描述
发现编译时候就不通过了,因为Tool这个泛型类已经指定为String类型了,则show方法参数也变成了String,这时候给他一个整形的数据也自然会报错了。那么我们能不能让它想show啥就show啥呢?答案是肯定的,这就是刚刚提到的泛型方法。

更改以下show方法:

public void <W> show(W w){
	System.out.println("show" + w.toString());
}

这样就发现主方法编译通过了。这就是泛型方法,顾名思义,就是将泛型定义在方法上。那么我们在拓展一下,将show方法改成静态的:
在这里插入图片描述
发现也报错了,这是因为静态方法可以通过类名.方法直接调用,而如果没创建Tool对象也就不知道T到底是什么类型的数据,所以报错了。这里要注意,当方法静态时,不能访问类上定义的泛型。如果静态方法想要使用泛型,则必须将泛型定义在方法上:

public static <W> void show(W w){
	System.out.println("show" + w.toString());
}

这样就没错误了,但是还要注意一点,泛型一定要放在返回值的前面修饰词的后面,如果将< W >放在static前面或者是void后面还是会报错的。

说完了泛型方法,再来说说泛型接口。有了前面的概念,泛型类就是泛型定义在类上,泛型方法就是泛型定义在方法上,那么泛型接口自然就是泛型定义在接口上面喽:

public class Test {
	public static void main(String args[]) {
		InterImpl1 in1 = new InterImpl();
		in1.show("hahaha");
	}
}

class InterImpl1 implements Inter<String>{
	@Override
	public void show(String str) {
		System.out.println(str);
	}
}

interface Inter <T> {
	public void show(T t);
}

可以看到,在最下面定义了一个Inter接口并让InterImpl1去实现它,在实现的同时将泛型定义为String,之后在主方法里面调用show方法,这便是泛型接口的一个简单例子。

接下来我们再得寸进尺一下,再定义一个InterImpl2实现Inter,但是定义的时候并不指定泛型类型,而是在实例化它的时候指定:

public class Test {
	public static void main(String args[]) {
		InterImpl2<Integer> in2 = new InterImpl2<Integer>();
		in2.show(5);
	}
}

class InterImpl2<T> implements Inter<T>{
	@Override
	public void show(T t) {
		System.out.println(t);
	}
}

interface Inter <T> {
	public void show(T t);
}

发现这样玩也是可以的,这便是泛型接口的两种常用的形式。这样泛型的基本用法就说完了,接下来的文章会说说泛型的一些高级用法。

猜你喜欢

转载自blog.csdn.net/weixin_44965650/article/details/106941117