先看文章目录,大致了解知识点结构,直接点击文章目录可以跳转到文章指定位置。
百度百科:Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型(通过泛型指定的类型来控制形参具体的类型),操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
①泛型的格式: <具体的数据类型>
② 集合加泛型以后,集合中只能保存具体的某一种数据类型。
ArrayList<String> list1 = new ArrayList<>();
③泛型只支持引用数据类型,不支持基本数据类型。
//报错
// ArrayList list2 = new ArrayList();
④等号左右两侧<>内的泛型实参类型不支持多态性,只能是相同类型
//报错
// List
①集合中是可以保存任意类型的对象。这些对象一旦保存到集合中之后,都会被提升成Object类型,通过对类型Object的引用来实现参数的“任意化”。
②“任意化”带来的缺点是取出数据时要显示的强制向下类型转换,可是取数据时我们并不知道是什么类型,使用保存的对象的特有方法或者属性时,需要向下转型并且向下转型还有风险,还得使用 instanceof关键字进行判断。
ArrayList list = new ArrayList();
list.add("Java");
list.add("python");
list.add("mysql");
//如下集合中添加其他引用数据类型,但是向下转型时我们不知道是什么类型数据,容易出错
//运行时才提示ClassCastException
// list.add(new Object());
for (Object o : list) {
//由于集合可以存储任意类型的对象,而这里只是适合String类型的强制类型转换,
//加入其他数据类型运行时会报classCastException类转换
String str = (String) o;
System.out.print(str.length()+" ");
}
总之很麻烦。于是出现了泛型解决这个问题
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("python");
list.add("mysql");
list.add("html");
list.add("javascript");
//加入泛型,变量的类型被限制,若加入其他引用类型编译阶段提示错误,add String in ArrayList cannot be applied to Object
//而不是运行时提示ClassCastException
// list.add(new Object());
for (String s : list) {
System.out.println(s.length());
}
泛型的好处:
①解决了集合中存储数据的不安全性;
②避免了运行时的异常,把运行时可能发生的异常,放在编译时作为编译错误处理了;
③省略了代码中的强制类型转换的书写;
①在类名或接口名后面使用<标识符>,在使用的时候,可以指定其中的类型。重点:使用的时候,指定其中的类型
②注意:不可以定义泛型数组
比如典型的容器类:在ArrayList类上有一个泛型的参数:E
当我们使用ArrayList的时候,我们要存储String类型就可以进行指定:
//指定ArrayList集合存储String类型
ArrayList<String> list = new ArrayList<>();
①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。
②泛型类不可以继承Exception类,即泛型类不可以作为异常被抛出。
③不可以用泛型构造对象,即:first = new T();是错误的
例子一:创建对象的时候,可以指定成员变量类型
//自定义泛型类:
class Demo<T>{
//num这个成员变量的类型为T,类型由外部指定
T num;
public void func(T num){
System.out.println(num);
}
}
// ----------------------------分割----------------------------------
public class TestGeneric_2 {
public static void main(String[] args) {
//创建Demo对象的过程中,明确泛型形参的具体类型 --> 定义泛型实参类型
//Demo d = new Demo<>(); T被指定为string了
Demo<String> d = new Demo<>();
d.func("abc");//abc
//Demo d = new Demo<>(); T被指定为Double了
Demo<Double> d2 = new Demo<>();
d2.func(123.23);//123.23
}
}
例子二:创建对象的时候,可以指定成员变量类型
//通过泛型,每次创建Student对象都可以指定成员变量score 的类型
class Student<E>{
//score这个成员变量的类型为E,类型由外部指定
E score;
public Student( E score) {//泛型构造方法形参的类型也为E,E的类型由外部指定
this.score = score;
}
public Student() { }
@Override
public String toString() {
return "Student{" + " score=" + score + '}';
}
}
// ----------------------------分割----------------------------------
public class TestGeneric_3 {
public static void main(String[] args) {
//通过泛型,每次创建Student对象都通过构造方法指定成员变量score 的类型
Student<String> s1 = new Student<>( "优秀");
//必须是类类型,不能是基本类型
Student<Integer> s2 = new Student<>( 88);
Student<Character> s3 = new Student<>('B');
System.out.println(s1);//Student{ score=优秀}
System.out.println(s2);//Student{ score=88}
System.out.println(s3);//Student{ score=B}
}
}
ps:
在原来的T后面用逗号隔开,写上其它的大写字母
//在原来的T后面用逗号隔开,写上其它的大写字母
public class More<T,U> {
private T x;
private T y;
private U name;
public T getX() { return x; }
public void setX(T x) { this.x = x; }
public T getY() { return y; }
public void setY(T y) { this.y = y; }
public U getName() { return name; }
public void setName(U name) { this.name = name; }
public More() { }
public More(T x, T y, U name) {
this.x = x;
this.y = y;
this.name = name;
}
public static void main(String[] args) {
//使用
More<Integer,String> morePoint = new More<Integer, String>(12,23,"尔康");
System.out.println(morePoint.getX()+morePoint.getY()+morePoint.getName());//35尔康
}
}
①在类名或接口名后面使用<标识符>,标识符可以是任意的字母、数字、下划线和 $ 。但是这里一般规范使用单个大写字母。
//自定义泛型接口
interface Inter<T>{
public abstract T show(T t);
}
// ----------------------------分割----------------------------------
//类实现接口的过程中明确泛型形参的具体类型
class InterImpl implements Inter<String>{
@Override
public String show(String s) {
System.out.println(s);
return s;
}
}
// ----------------------------分割----------------------------------
//类实现接口的过程中没有明确泛型形参的具体类型
class InterImpl2<E> implements Inter<E> {
@Override
public E show(E e) {
System.out.println(e);
return e;
}
}
// ----------------------------分割----------------------------------
public class TestGeneric_5 {
public static void main(String[] args) {
//在实例化对象的过程中明确泛型形参的具体类型
InterImpl2<Integer> ii = new InterImpl2<>();
ii.show(123);//123
//在实例化对象的过程中去除了<>的定义 ==> 称为:泛型的擦除操作
//理解:所有的泛型形参类型位置都同步为Object类型
InterImpl2 ii2 = new InterImpl2();
ii2.show("abc");//abc
}
}
①有时方法需要接收的数据类型和类上外界指定的类型不一致。这时我们可以在这个类中的这个方法上单独给这个方法设定泛型。
②静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。
class Demo2<A>{
//静态方法中不能使用类的泛型,因为类的泛型是在实例化之后才创建,而静态方法是在实
// 例化之前就被创建了的。一个存在的东西不能调用一个不存在的东西。
// public static void func(A a) { System.out.println(a); }//这会报错噢
public void func(A a) { System.out.println(a); } //这不会
}
③泛型方法,可以声明为静态的。原因:泛型参数是在调用的时候确定的,并非是在实例化类的时候确定。
class Demo2<A>{
public void func(A a) { System.out.println(a); }
public static <B> void method(B b) {
System.out.println(b);
}
}
//------------------------分割------------------------------
public class TestGeneric_4 {
public static void main(String[] args) {
//实例化对象的过程中,就明确了泛型形参A的具体类型,但是B类型仍然不明确
Demo2<String> d = new Demo2<>();
d.func("hello");
//在调用method方法传入实参数据的一刹那,B类型的具体类型就明确了
d.method(123);
}
}
④泛型方法只是针对于方法本身来讲,与方法所在的类或接口是否为泛型类、泛型接口没有关联。
public class TestGeneric_6 {
public static void main(String[] args) {
method(123);
}
public static <B> void method(B b) {
System.out.println(b);
}
}
//自定义泛型方法 遍历不同类型的数组元素
public static <T> void printArray(T[] arr) {
for (T t : arr) {
System.out.print(t + " ");
}
System.out.println();
}
//------------------------分割-----------------------
public static void main(String[] args) {
String[] arr = {"aaa", "bbb", "ccc", "ddd"};
Integer[] arr2 = {123, 456, 789};
printArray(arr);
printArray(arr2);
}
①? 表示泛型的通配符,表示集合中的任意数据类型,传递过来的表示什么数据类型,?就表示什么数据类型
②现在需要定义一个方法既可以接收ArrayList,又可以接收HashSet集合容器对象,只能使用它们的父类或接口类型来接收,这样就可以使用Collection接口类型接收。但是使用泛型不知道具体的数据类型,可以使用泛型的通配符 ? 来表示。
//自定义方法:遍历打印Collection集合元素
//这里书写Object不可以,因为泛型格式要求两侧必须类型一致,所以使用?通配符
//Collection
public static void printCollection(Collection<?> coll) {
for (Object o : coll) {
System.out.print(o + " ");
}
System.out.println();
}
//------------------------分割-------------------------------
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("abc");
list.add("def");
HashSet<Integer> set = new HashSet<>();
set.add(123);
set.add(456);
printCollection(list); //参数位置:Collection coll = new ArrayList()
printCollection(set); //参数位置:Collection coll = new HashSet()
}
}
①上限通配符:<? extends T>,泛型中的参数必须是 T 或者 T 的子类
②下限通配符:<? super T>,泛型中的参数必须是 T 或者 T 的超类
③无限定通配符:>,类型参数可以是任何类型
public class TestGeneric_2 {
public static void main(String[] args) {
//创建Person和其子类型的集合容器
ArrayList<Person> pers = new ArrayList<>();
ArrayList<Student> stus = new ArrayList<>();
HashSet<Teacher> teas = new HashSet<>();
//创建Animal和其子类型的集合容器
ArrayList<Animal> ans = new ArrayList<>();
ArrayList<Dog> dogs = new ArrayList<>();
HashSet<Cat> cats = new HashSet<>();
//遍历集合元素
printCollection(pers);
printCollection(stus);
printCollection(teas);
// printCollection(ans);//报错
// printCollection(dogs);//报错
// printCollection(cats);//报错
}
//printCollection接受的参数必须是Person以及它的子类
public static void printCollection(Collection<? extends Person> coll) {
for (Person ps : coll) {
System.out.print(ps + " ");
}
System.out.println();
}
}
class Person{}
class Student extends Person{}
class Teacher extends Person{}
class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}