JAVA泛型的使用(超详细)
Posted 王小麻子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA泛型的使用(超详细)相关的知识,希望对你有一定的参考价值。
目录
1、概念
Java泛型是JDK1.5中引⼊的⼀个新特性,其本质是参数化类型,把类型作为参数传递。 常⻅形式有泛型类、泛型接⼝、泛型⽅法。
2、优点
1)编译时,检查添加元素的类型,提高了安全性
2)减少了类型转换次数,提高了效率
public class Test public static void main(String[] args) ArrayList<Dog> arrayList = new ArrayList<>(); arrayList.add(new Dog("贝贝",5)); arrayList.add(new Dog("乐乐",3)); arrayList.add(new Dog("小黑",4)); //arrayList.add(new Cat("贝贝",5)); 错误 for (Dog dog : arrayList) System.out.println(dog.getName()+"-"+dog.getAge());
3、泛型使用实例
public class Test public static void main(String[] args) HashSet<Student> hashSet = new HashSet<>(); hashSet.add(new Student("张三",22)); hashSet.add(new Student("李四",23)); hashSet.add(new Student("王五",24)); //遍历 for (Student student : hashSet) System.out.println(student); //使用泛型方式给HashMap放入三个学生对象 HashMap<String, Student> hashMap = new HashMap<>(); hashMap.put("Tom",new Student("Tom",22)); hashMap.put("Jack",new Student("Jack",23)); hashMap.put("Smith",new Student("Smith",24)); //迭代器 entrySet Set<Map.Entry<String, Student>> entrySet = hashMap.entrySet(); Iterator<Map.Entry<String, Student>> iterator = entrySet.iterator(); System.out.println("==========================="); while (iterator.hasNext()) Map.Entry<String, Student> next = iterator.next(); System.out.println(next.getKey()+"-"+next.getValue());
4、泛型使用细节
1)interface List<T>,public class HashSet<E>...等等
T、E只能是引用类型
ArrayList<Integer> list1 = new ArrayList<>(); //正确 ArrayList<int> list2 = new ArrayList<int>(); //错误
2)在给泛型指定具体类型后,可以传入该类型或者其子类类型
public class Demo03 public static void main(String[] args) //因为E制定了A类型,构造器传入了new A //在给泛型指定具体类型后,可以传入该类型或者其子类类型 Pig<A> pig = new Pig<A>(new A()); Pig<A> pig1 = new Pig<A>(new B()); class A class B extends A class Pig<E> E e; public Pig(E e) this.e = e;
3)在实际开发中,我们往往简写
ArrayList<Integer> list = new ArrayList<Integer>(); //简写 ArrayList<Integer> list1 = new ArrayList<>();
5、自定义泛型类
注意细节:
1)普通成员可以使用泛型
public class AA<T,R> T t; R r;
2)使用泛型的数组,不能初始化
public class AA<T,R> T t; R r; T[] ts; //T[] ts =new T[4]; 不能初始化
3)静态方法中不能使用类的泛型
//因为静态时和类相关,在类加载时,对象还没创建,所以静态属性和静态方法使用了泛型,JVM就无法完成初始化 // static R r2; // public static void m(T t)
4)泛型类的类型是在创建对象时确定的
5)如果创建对象时没有指定类型,默认为Object
6、自定义泛型接口
注意细节:
1)接口中,静态成员也不能使用泛型
2)泛型接口的类型,在继承接口或实现接口时确定
3)没有指定类型,默认为Object
public interface IUsb<U,R> int n=10; //U name; 不能这样使用,静态成员不能使用泛型 //普通方法,可以使用接口泛型 U get(U u); void hi(R r); void run(R r1,R r2,U u1,U u2); //在JDK8中,可以在接口中,使用默认方法,也是可以使用泛型 default R method(U u) return null;
public interface IA extends IUsb<String, Double>
public class AA implements IA //当我们去实现IA接口时,因为IA在继承IUsb接口时,制定了U为String,R为Double //在实现IUsb接口的方法时,使用String替换U,Double替换R @Override public String get(String s) return null; @Override public void hi(Double aDouble) @Override public void run(Double r1, Double r2, String u1, String u2)
7、自定义泛型方法
注意细节:
1)泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car//普通类 public void run()//普通方法 public<T,R> void fly(T t,R r)//泛型方法 class fish<T,R>//泛型类 public void run() public <U,M>void eat(U u, M m)
2)当泛型方法被调用时,类型会确定
public class Test public static void main(String[] args) Car car = new Car(); //当调用方法时,传入参数,编译器就会确定类型 car.fly("宝马",100);
3) public void swim(T t)不是泛型方法,而是使用了类声明的泛型。
java中的泛型
本文将详细介绍java泛型的用法以及泛型的原理
java泛型
泛型是在J2 SE1.5中引入的一个特性 可以将类型抽象为一个参数 从而简化代码和实现类型安全
如何使用泛型
泛型可以用于方法 类和接口 通过尖括号加标识符的方式声明
class GenericityClass<T> { T t; public GenericityClass(T t) { this.t = t; } public T getT() { return t; } } interface GenericityInterface<T> { void printGenericity(T t); } public static <A> A test(A a) { System.out.println(a); return a; }
其中类型T是一个不确定的类型 需要填入实际类型才能使用
ps1: 可以给泛型参数起任意名称 例如 <T2333> <abcd> <FAN_XING>
ps2: 可以声明任意个泛型参数 不同的泛型参数用‘,‘隔开 例如 <T, E, TD250>
使用泛型类,方法与普通的类,方法并没有太大区别 除了要填入尖括号和类型
GenericityClass<String> gc = new GenericityClass<String>("泛型类测试"); String s = gc.getT(); Main.<String>test("泛型方法测试");
因为编译器推导的存在 我们可以省略一些重复的类型信息
GenericityClass<String> gc = new GenericityClass<>("泛型类测试");// 前边的<String>已经声明了类型 后边的就可以省略 String s = gc.getT(); Main.test("泛型方法测试");// 后边填入的参数String已经确定了泛型参数的类型 因此可以省略<String>
java泛型的实现原理
java的泛型实现机制并不是类似c++编译时模板生成 也不是c#运行时生成泛型类 而是利用了一套泛型擦除机制
通过观察编译后的字节码 我们发现 编译器将所有的泛型参数都处理成Object类型
class GenericityClass { Object t; public GenericityClass(Object t) { this.t = t; } public Object getT() { return t; } } interface GenericityInterface { void printGenericity(Object t); } public static Object test(Object a) { System.out.println(a); return a; }
GenericityClass gc = new GenericityClass("泛型类测试"); String s = (String) gc.getT();// 因为返回值是Object所以要强制转换 BlueBridge.test("泛型方法测试");
我们可以用反射来验证这一点 如果是泛型擦除 那么List<String>的实际类型应该是List<Object>
List<String> list = new ArrayList<>(); list.add("这是String"); list.add("这也是String"); list.add("这还是String"); try { List.class.getMethod("add", Object.class).invoke(list, (Object) new int[] {1, 2, 3}); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); } for (Object obj : list) { System.out.println(obj); }
运行结果
这是String
这也是String
这还是String
[I@4554617c
于是我们在一个List<String>中成功插入了一个int数组
为什么java要使用泛型擦除机制来实现泛型
为了兼容旧版本代码
java的泛型是在J2 SE1.5中加入的 在此之前的通用容器都是将对象转为Object储存 List<T>编译后就成了List 完美兼容旧版本
但是这也带来了一些缺陷 泛型不能是基本类型 所有基本类型必须转为对应的包装类 例如List<Integer> 这个过程中产生了装箱 导致效率损失
听说之后版本的java会引入一些机制来解决基本类型装箱问题 但那可能得等到猴年马月了
END
以上是关于JAVA泛型的使用(超详细)的主要内容,如果未能解决你的问题,请参考以下文章