JAVA泛型的使用(超详细)

Posted 王小麻子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA泛型的使用(超详细)相关的知识,希望对你有一定的参考价值。

目录

1、概念

2、优点

3、泛型使用实例

4、泛型使用细节

5、自定义泛型类

6、自定义泛型接口

7、自定义泛型方法


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泛型的使用(超详细)的主要内容,如果未能解决你的问题,请参考以下文章

求java泛型的详细讲解,最基础的,我去网上博客里的啥都比较高深,看不懂

泛型的内部原理:类型擦除以及类型擦除带来的问题

java中的泛型

java获取泛型class

java教程——泛型

java中的泛型 求详细解释