一、概念
1、概念
泛型又称参数化类型,从JDK1.5开始出现,用于解决数据类型的安全性问题。
2、优点
-
限制所使用的数据类型,避免使用不恰当的数据类型
-
在代码编译之后,泛型会自动帮我们进行类型强转(默认是Object类型),降低手动类型强转可能产生的类型转换异常风险
-
代码重用,减少代码编写的工作量
3、特点
-
如果不指定泛型,那么泛型默认是
Object
类型
-
泛型不能是基础数据类型,但是可以是基础数据类型的包装类,原因是:泛型默认是Object类型,上面已经解释过了,这里不在赘述~
-
泛型具体的类型是在编译时确定的,如果我们指定泛型为具体类型,那就使用它。否则使用Object类型
-
泛型使用位置:接口、类、非静态方法(参数、返回值、代码)、非静态成员属性、内部类等等
-
为什么不能在静态方法或者静态成员属性上使用泛型呢?
答:静态方法和静态成员变量属于类,无法在编译时确定类型,所以不能使用泛型 -
如果普通成员变量是数组类型,不可以赋初始值,比如:T[] arr = new T[8],这种写法是不允许的,原因是:参数类型T不能直接实例化
二、使用方法
1、类
代码:
// 解说:目的是演示在类上使用泛型,但是其实也演示了在构造器、方法、成员属性中使用泛型
public class Test {
public static void main(String[] args) throws Exception {
// 在类上使用单个泛型
Cat<String> cat = new Cat<>("小花");
System.out.println("小猫的名字:" + cat.getT());
// 在类上使用多个泛型
Person<String, Integer> person = new Person<>("小明", 180);
System.out.printf("\n学生姓名:%s,身高:%s厘米", person.getE() , person.getT());
}
}
// 案例1:在类上使用单个泛型
class Cat<T> {
T t;
public Cat(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
// 案例2:在类上使用多个泛型
class Person<T, E> {
T t;
E e;
public Person(T t, E e) {
this.t = t;
this.e = e;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
}
解释:
泛型其实帮我们限制了数据类型,然后进行了数据的强制类型转换
如果没有泛型,那我们需要自己注意使用的数据类型,并且自己直接强制类型转换,这些工作无趣又繁琐,所以感谢泛型的出现
为证明我在解释中说的第一句话,我将上述示例中的Test.java生成的Test.class进行反编译后,我们看下结果:
解释中第一句话前半截在介绍泛型优点的时候已经提到了,这里解释下这句话的后半截内容
2、普通成员属性
请看上一节1、类
请注意: 在成员属性中使用的泛型,必须来自于在类上定义的
3、接口
代码:
public class Test {
public static void main(String[] args) throws Exception {
Cat<String, String> cat = new Cat<>();
cat.eat("猫粮");
Dog dog = new Dog();
dog.eat("狗粮");
}
}
// 接口
interface Animal<T, E> {
E eat();
void eat(T t);
}
// 类(实现接口的时候指定一样的泛型类型,类后面指定的可以大于等于接口后面携带的)
class Cat<T, E> implements Animal<T, E> {
@Override
public E eat() {
return null;
}
@Override
public void eat(T t) {
System.out.println("小猫咪吃到猫粮了~");
}
}
// 类(在继承类的时候指定泛型具体类型)
class Dog implements Animal<String, String> {
@Override
public String eat() {
return "hello world~";
}
@Override
public void eat(String s) {
System.out.println("小狗吃到狗粮了~");
}
}
结果:
小猫咪吃到猫粮了~
小狗吃到狗粮了~
4、方法
代码:
public class Test {
public static void main(String[] args) throws Exception {
Cat cat = new Cat();
cat.eat("饼干", "小鱼干");
Dog<String> dog = new Dog<>();
dog.eat("狗粮");
Fox<String> fox = new Fox<>();
fox.eat("兔子", "小鸡");
}
}
// 在普通类中使用泛型方法
class Cat {
public <T, E> T eat(T t, E e) {
System.out.println("小猫咪吃到猫粮了~");
return t;
}
}
// 在泛型类中使用泛型方法,并且在泛型方法中使用泛型类的泛型
class Dog<T> {
public T eat(T t) {
System.out.println("小狗吃到狗粮了~");
return t;
}
}
// 泛型方法和泛型类一起使用
class Fox<T> {
public<E> T eat(T t, E e) {
System.out.println("狐狸吃到兔子了~");
return t;
}
}
结果:
小猫咪吃到猫粮了~
小狗吃到狗粮了~
狐狸吃到兔子了~
5、继承和通配符
代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
A a = new A();
a.eat(Arrays.asList("hello", "world"));
List<Son> sonList = new ArrayList<>();
a.play(sonList);
List<Parent> parentList = new ArrayList<>();
a.run(parentList);
}
}
// 测试类
class A {
// 支持所有泛型类型,类似于Object
void eat(List<?> list) {
System.out.println("eat方法执行了~");
}
// 支持范围小于等于Parent类的类,super后面的系统决定了下限
void play(List<? extends Parent> list) {
System.out.println("play方法执行了~");
}
// 支持范围大于等于Son类的类,super后面的系统决定了下限
void run(List<? super Son> list) {
System.out.println("run方法执行了~");
}
}
// 父类接口和子类接口,用于测试extends和super关键字
class Parent {}
class Son extends Parent {}
结果:
eat方法执行了~
play方法执行了~
run方法执行了~