不点蓝字,我们哪来故事?
泛型概念
泛型,即“参数化类型”。就是将类型由原来具体的类型参数化变成参数形式,然后在使用时传入具体的类型。类似于方法中的变量参数。
字我都认识,咋放在一起就看不懂了呢???
Don't be afraid, I'm here.
‘泛’,意为宽泛的,普遍的,是相对于具体的,独特的而言的。所以泛型是一种广泛的,具有代表性的类型,而如int, float,String等则是具体的类型。
“参数化类型”只是泛型的一个显得很高端的别名,大家不要被吓到。泛型用一个具有代表性的参数来代表各种类型,因此该类型也定义成参数形式将其参数化,故又称为参数化类型。
引入
下面我们举个例子来看,同时也可以帮助大家更好地理解泛型(参数化类型)的概念。
现在有一个需求:对于同一个方法,当A调用时要传入int类型的参数,当B使用时要传入float类型的参数,而当C调用时要传入String类型的参数,请问你要如何编写这个方法?
very good! 给想到重载的同学点个赞,使用重载确实可以解决这个需求,但是当我有更多的类型需求时你是不是还要继续重载呢?一方面这使得我们要经常维护代码,另一方面相同的代码很多,不符合开发规范,也不好。
有没有其他的方法呢?
对的,用Object作为参数类型也可以,但是装入的数据类型都被当作Object类型,从而丢失了实际类型,而且获取数据时往往需要转型,效率低还更容易出现错误,也不合适。
还有没有其他的方法呢?哈哈,忘了我们今天的主题了吗?
泛型嘛,说白了不就是模板嘛,看代码:
定义了一个泛型方法,然后在主方法中多次调用且每次传递不同的参数。
package run.xyzliu.section2.note;
public class GenericParadigm {
public static void main(String[] args) {
//传递String类型
print("hello");
//传递int类型
print(100);
//传递字符类型
print('H');
//传递float类型
print(20.7);
}
public static void print(A a){
System.out.println(a);
}
}
有没有一种拿了同学的报告改了个名字的感觉?
泛型使用
接下来我们就学习一下如何优雅地改同学报告使用泛型
格式
使用
<>
符号
使用规则
不能使用静态属性和静态方法上
使用的时候需要指定数据类型
. 编译时会检查数据的类型
. 获取数据不再需要强制类型转换泛型使用是不能指定基本数据类型,只能使用起对应的包装类
泛型类
在一个类创建时就声明类中有种类型是我们泛指的类型,在类名后使用声明,其中T可以替换成任意字母。
定义泛型类的格式:
public class className {
private T data;
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
泛型类的使用
定义泛型类Person:
package run.xyzliu.section2.note;
//创建类的时声明一个泛型,用参数A表示
public class Person <A>{
private String name;
private int age;
private A data;
/*name和age的getter()setter()方法*/
//获取data的方法类型用泛型表示
public A getData() {
return data;
}
//设置data的方法传入参数也是泛型
public void setData(A data) {
this.data = data;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", data=" + data +
'}';
}
}
在Test类中使用泛型类Person:
package run.xyzliu.section2.note;
public class Test {
public static void main(String[] args) {
//使用泛型类,给声明的泛型传入具体地String类型
Person p1 = new Person<>();//使用p1调用setData()方法传入的应该时String类型的参数
p1.setData("小华学IT");
System.out.println("使用泛型传入String类型:" + p1.getData());//使用泛型,给声明的泛型传入具体的
Person p2 = new Person<>();//此时使用p2调用setData()方法传入的则应是Integer类型的参数
p2.setData(1000);
System.out.println("使用泛型传入Integer类型:" + p2.getData());
}
}
泛型接口
有了使用泛型类的经验,使用泛型接口就容易多了,定义格式如下:
public interface IntercaceName {
T getData();
}
实现接口时,可以选择指定泛型类型,也可以选择不指定, 如下:
指定类型:
public class Interface1 implements IntercaceName<String> {
private String text;
@Override
public String getData() {
return text;
}
}
不指定类型:
public class Interface1<T> implements IntercaceName<T> {
private T data;
@Override
public T getData() {
return data;
}
}
这里不再展示代码,有兴趣的朋友可以自己动手使用一下。
泛型方法
定义泛型方法的格式:
private static T 方法名(T a, T b) {}
此时定义的泛型只在方法内有效,方法外无法使用。代码可参考“引入”种的泛型方法。建议亲自上机写写几遍。
泛型限制类型
在此之前我们设置的泛型使用可以代替任何类型,但是有些特殊的需求,要求泛型的范围必须某范围以内。
在使用泛型时, 可以指定泛型的限定区域。例如:必须是某某类的子类或 某某接口的实现类
格式:
下面通过一段代码展示:
类中定义了一个Fruit(水果)接口及其实现类Apple(苹果),随后又定义了一个Plate(盘子)类。目前没有对泛型做任何限制,所以可以向泛型类中传入各种具体的类型。
package run.xyzliu.section2.note;
public class Demo {
public static void main(String[] args) {
Plate p = new Plate<>();
p.data = "小华学IT";
System.out.println(p.data);
p.data = "1000";
System.out.println(p.data);
}
}interface Fruit{}class Apple implements Fruit{}class Plate<T> {
T data;
}
现在要求这个盘子只能装水果,不能装其他的物品,请问使用泛型如何实现?
其实很简单,只需给泛型添加一个限制即可。使其继承水果及其子类即可,如下:
class Plate < T extends Apple>
此时我们在向泛型传入其他类型便会报错,只能传入水果子类:
泛型中通配符 "?"
代替方法具体的类型实参。
1 指定了泛型类型的上届
2 指定了泛型类型的下届
3 指定了没有限制的泛型类型
有兴趣的朋友可以按照格式自行上机练习使用。
泛型作用/优点
经过上面的介绍,我们可以得出泛型的作用,如下:
提高代码复用率
泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
注意事项
在编译之后程序会采取去泛型化的措施。
也就是说Java中的泛型,只在编译阶段有效。
在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加。
类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
通过这篇文章,你了解泛型了吗?
坚持原创
坚持干货
小华学IT
关注小华 一起学习 一起进步