文章目录
- 1. 为什么要有泛型Generic
- 2. 泛型怎么用
- 2.1. 泛型类
- 2.2. 泛型接口
- 2.3. 泛型方法
- 3. 泛型通配符
- 3.1. 通配符
- 3.2. 有限制的通配符
1. 为什么要有泛型Generic
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
举例:
package day11;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List list = new ArrayList<>();
//不指定泛型,可以向list集合中添加各种类型元素
list.add(111);
list.add("sss");
list.add(true);
//需求,只能在集合中存字符串
List<String> l = new ArrayList<String>();
l.add("xx");
// l.add(111);//指定的String类型,不可以添加String以外的元素类型
}
}
Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
解释:
视频中老师说:比如上段代码中最后一行的l.add(111)
在编译期间就会报错,只有删除这一行,或者将里面的数据改成String类型后,才可以通过编译。
2. 泛型怎么用
2.1. 泛型类
对象实例化时不指定泛型,默认为: Object 。
泛型不同的引用不能相互赋值。
package day11;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
A<String> a1 = new A<String>();//在new A的对象的时候,指定泛型的类型String
a1.setKey("xxxx");//对象使用setKey(T key)方法,中的key形参就是String
System.out.println(a1.getKey());//T getKey(),返回值就有new对象确定返回值是String
A<Integer> a2 = new A<Integer>();
a2.setKey(1);
Integer i = a2.getKey();
A a3 = new A();//不指定泛型,相当于指定了一个Object类型
//A<Object> a3 = new A<Object>();//等同于上行代码
a3.setKey(new Object());
Object obj = a3.getKey();
//同样的类,但是在new对象时指定不同的数据类型,那么这些对象不能互相赋值
a1 = a2;//错误,不同的数据类型不能互相赋值
}
}
/**
* 此处的泛型‘T’可以任意的取名,即可换成A,B,C等
* 一般使用T,译为type
* @author 14532
*
* @param <T>
*/
class A<T>{//< >中的内容可以任何取名,按惯例是T
private T key;
public void setKey(T key) {
this.key = key;
}
public T getKey() {
return this.key;
}
}
2.2. 泛型接口
定义一个泛型接口:
interface Generator<T> {
T next();
}
①未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
②如果实现
接口时,指定接口的泛型的具体数据类型,这个类实现接口所有方法的位置都要替换实际的具体数据类型
举例:
package day11;
public class Test1 {
public static void main(String[] args) {
B1<Object> b1 = new B1<Object>();
B1<String> b11 = new B1<String>();
B2 b2 = new B2();//不用指定泛型,指定泛型反而会报错
}
}
interface IB<T>{
T test(T t);
}
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* @author 14532
*
* @param <T>
*/
class B1<T> implements IB<T>{//B1<T>:未传入实参,将泛型的声明<T>也一起加到类中
@Override
public T test(T t) {
return t;
}
}
/**
* 如果实现接口时指定接口的泛型的具体数据类型
* 这个类实现接口所有方法的位置都要替换实际的具体数据类型
* @author 14532
*
*/
class B2 implements IB<String>{
@Override
public String test(String t) {
return null;
}
}
2.3. 泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型化的。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:
public class DAO {
public <E> void show(E e){
System.out.println(e.toString());
}
public <T> T show1(T t){
return t;
}
}
举例:
class Cc{
/**
* 无返回值的泛型方法
* @param <T>
* @param s
*/
public <T> void test(T s) {
T t = s;
}
/**
* 有返回值的泛型方法
* @param <T>
* @param s
* @return
*/
public <T> T test1(T s) {
return s;
}
/**
* 形参为可变参数的泛型方法
* @param <T>
* @param strs
*/
public <T> void test2(T... strs) {
for(T s : strs) {
System.out.println(s);
}
}
}
举例2:
package day11;
public class Test1 {
public static void main(String[] args) {
Cc<Object> c = new Cc<Object>();
c.test("xxx");
//泛型方法在调用之前没有固定的数据类型
//在调用时,传入的参数是什么类型,就会把泛型改成什么类型
//也就是说,泛型方法会在调用时确定泛型的具体数据类型
Integer i = c.test1(2);//传递的参数是Integer,泛型就固定成Integer,返回值就是Integer
Boolean b = c.test1(true);//传递的参数是Boolean,泛型就固定成Boolean,返回值就是Boolean
}
}
class Cc<E>{
private E e;
// public static void test3() {
// //在静态方法中不能使用类定义的泛型,如果要使用泛型,只能使用静态方法自己定义的泛型
// //System.out.println(this.e);
// }
/**
* 静态方法的泛型方法
* @param <T>
* @param t
*/
public static <T> void test3(T t) {
//在静态方法中不能使用类定义的泛型,如果要使用泛型,只能使用静态方法自己定义的泛型
System.out.println(t);
}
/**
* 无返回值的泛型方法
* @param <T>
* @param s
*/
public <T> void test(T s) {
//在类上定义的泛型,可以在普通的方法中使用
System.out.println(this.e);
T t = s;
}
/**
* 有返回值的泛型方法
* @param <T>
* @param s
* @return
*/
public <T> T test1(T s) {
return s;
}
/**
* 形参为可变参数的泛型方法
* @param <T>
* @param strs
*/
public <T> void test2(T... strs) {
for(T s : strs) {
System.out.println(s);
}
}
}
3. 泛型通配符
3.1. 通配符
不确定集合中的元素具体的数据类型,使用"?"表示所有类型
如:
public void test(List<?> list){
System.out.println(list);
}
举例:
package day11;
import java.util.ArrayList;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
Dd d = new Dd();
List<String> l1 = new ArrayList<String>();//?:可以代表各种类型,如String、Integer
d.test(l1);
List<Integer> l2 = new ArrayList<Integer>();
d.test(l2);
}
}
class Dd {
public void test(List<?> list) {//test方法需要一个list集合的参数,不确定list集合中到底是存的数据的类型是什么
}
}
3.2. 有限制的通配符
举例:
<? extends Person> (无穷小, Person)
:
只允许泛型为Person及Person父类的引用调用
<? super Person> [Person, 无穷大]
:
只允许泛型为Person及Person父类的引用调用
<? extends Comparable>
:
只允许泛型为实现Comparable接口的实现类的引用调用
举例:
package day11;
import java.util.ArrayList;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
Dd d = new Dd();
//list参数的元素数据类型是CC1及其子类
List<CC1> lc = new ArrayList<CC1>();
d.test1(lc);
List<DD1> ld = new ArrayList<DD1>();
d.test1(ld);
// List<BB1> lb = new ArrayList<BB1>(); //BB1不是CC1的子类
// d.test1(lb);
//list参数的元素数据类型是CC1及其父类
d.test2(lc);
List<BB1> lb = new ArrayList<BB1>();
d.test2(lb);
List<AA1> la = new ArrayList<AA1>();
d.test2(la);
// d.test2(ld);//DD1不是CC1的父类
//list参数的元素数据类型是IA的实现类
List<IAImpl> lia = new ArrayList<IAImpl>();
d.test3(lia);
// d.test3(la);//AA1不是IA的实现类
}
}
class Dd {
public void test(List<?> list) {//test方法需要一个list集合的参数,不确定list集合中到底是存的数据的类型是什么
}
public void test1(List<? extends CC1> list) {//list参数的元素数据类型是CC1及其子类
}
public void test2(List<? super CC1> lsit) {//参数的元素数据类型是CC1及其父类
}
public void test3(List<? extends IA> lsit) {//list参数的元素数据类型是IA的实现类
}
}
class AA1{}
class BB1 extends AA1{}
class CC1 extends BB1{}
class DD1 extends CC1{}
interface IA{}
class IAImpl implements IA{}