JAVA泛型(一)泛型基础
泛型入门
使用泛型
Java5之后,Java引入了“参数化类型”的概念,允许程序在创建集合时指定元素的类型。
import java.util.ArrayList; import java.util.List; public class a{ public static void main(String[] args){ List<String> strList = new ArrayList<String>(); strList.add("书本1"); strList.add("书本222"); //下面代码将引起编译错误 //strList.add(5); //不需要进行强制类型转换 //strList.forEach(str -> System.out.println((String)str.length())); strList.forEach(str -> System.out.println(str.length())); } }
Java9增强的"菱形"语法
import java.util.*; public class a{ public static void main(String[] args){ //Java自动推断ArrayList的<>应该是String List<String> strList = new ArrayList<>(); //Java自动推断HashMap的<>应该是String,List<String> Map<String,List<String>> schoolsInfo = new HashMap<>(); } }
Java9再次增强了"菱形"语法,它甚至允许在创建匿名内部类时使用菱形语法,Java可根据上下文来推断匿名内部类中泛型的类型
import java.util.*; interface Foo<T>{ void test(T t); } public class a{ public static void main(String[] args){ //指定Foo类中泛型为String Foo<String> f = new Foo<>(){ public void test(String t){ System.out.println("test方法的参数t为:" + t); } }; //使用泛型通配符,此时相当于通配符的上限为Object Foo<?> fo = new Foo<>(){ //test()方法的参数类型为Object public void test(Object t){ System.out.println("test方法的Object参数为:" + t); } }; //使用泛型通配符,通配符的上限为Number Foo<? extends Number> foo = new Foo<>(){ //test()方法的参数类型为Number public void test(Number t){ System.out.println("test方法的Number参数为:" + t); } }; } }
深入泛型
定义泛型接口、类
所谓泛型,就是允许在定义类,接口时使用类型形参,这个类型形参(或叫泛型)将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型实参)。
下面是Java5改写后的List接口、Iterator接口、Map接口的代码片段
import java.util.*; public interface List<E>{ //在该接口里,E可作为类型使用 //下面方法可以使用E作为参数类型 void add(E x); Iterator<E> iterator(); ... } public interface Iterator<E>{ E next(); ... } public interface Map<K,V>{ Set<K> keySet(); V put(K key,V value); ... }
包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑上的子类,但这种子类在物理上并不存在。
可以为任何类、接口增加泛型声明(集合类是泛型的重要使用场所):
public class Apple<T>{ private T info; public Apple(){} public Apple(T info){ this.info = info; } public void SetInfo(T info){ this.info = info; } public T getInfo(){ return this.info; } public static void main(String[] args){ //由于传给T形参的是String,所以构造器参数只能是String Apple<String> a1 = new Apple<>("苹果"); System.out.println(a1.getInfo()); //由于传给T形参的是Double,所以构造器参数只能是Double或double Apple<Double> a2 = new Apple<>(5.2); System.out.println(a2.getInfo()); } }
从泛型类继承派生子类
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含泛型形参。例如,下面的代码就是错误的。
//定义类A继承Apple类,Aplle类不能跟泛型形参 public class A extends Apple<T>{}
如果想从Apple类派生一个子类,则可以改为如下代码:
//使用Apple类时T形参传入String类型 public class A extends Apple<String>
调用方法时必须为所有的数据形参传入参数值,与调用方法不同的是,使用类、接口时也可以不为泛型形参传入实际的类型参数:
//当使用Apple类时,没有为T形参传入实际的类型参数 public class A extends Apple
像这种省略Apple类时省略泛型的形式本称为原始类型(raw type)。编译器会发出警告,此时,系统会把Apple<T>类里的T形参当成Object类型处理。
public class A1 extends Apple{ public String getInfo(){ //super.getInfo()方法返回值是Object类型 //所以加toString()才返回String类型 return super.getInfo().toString(); } }
public class A1 extends Apple<String>{ //正确重写了父类的方法,返回值 public String getInfo(){ return "子类" + super.getInfo(); } //下面方法是错误的,重写父类方法时返回值类型不一致 public Object getInfo(){ return "子类"; } }
并不存在泛型类
不管为泛型形参传入哪一种类型实参,对于Java来说,它们依然被当成同一个类处理,在内存里也只占用一块内存空间,因此在静态方法、静态初始化块或静态变量的声明和初始化中不允许使用类上定义的泛型形参。
//下面两条代码错误 static T info; public static void bar(T mas){}
由于系统并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类。
java.util.Collection<String> cs = new java.util.Collection<>(); //下面代码错误 if(cs instanceof java.util.ArrayList<String>){...}
浙公网安备 33010602011771号