专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java基础——泛型(java泛型简单例子)

temp10 2024-10-16 15:55:59 java教程 12 ℃ 0 评论

1 基本概括

2 主要介绍

2.1 什么是java泛型?

Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

Java基础——泛型(java泛型简单例子)

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

2.2 特性

泛型只在编译阶段有效。看下面的代码:

List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
    Log.d("泛型测试","类型相同");
}


对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

2.2 泛型的使用

泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

2.2.1 java泛型类

泛型类:必须先声明,再使用;声明是通过实现的;约定泛型可以使用单个大写字母T、E、K、V等表示

语法:

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....

  }
}



public class GenericsSimpleExample {
    public static void main(String[] args) {
        A<String , Integer> a0 = new A<>();
        a0.t= "testStr";
        A<Integer , Integer> a1 = new A<>();
        a1.t = 100;
         //  泛型参数在使用时要引用数据类型,不能是基本数据类型,比如int等等
        //A<Integer , int> a2 = new A<>();
        // 泛型类如果不指定具体的参数类型,默认数据类型是object
        Class<A> cls = A.class;
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + ":" + field.getType().getSimpleName());
        }
        // 泛型类在具体实现上可以看出不同数据类型,实践上是相同的数据类型,可以通过hashcode进行验证
        System.out.println(a0.getClass().hashCode());
        System.out.println(a1.getClass().hashCode());
    }
    static class A <T , E> {
        public T t;
        public void test0(T  t) {
        }
    }
}

注意要点:泛型类的参数类型不能是基本数据类型,要使用引用数据类型泛型类在不指定具体数据类型时候,默认是使用object类型的泛型类具体使用上可以使用不同的数据类型,实践上是相同的数据类型

从泛型类派生子类

1、子类是泛型的情况,子类要和父类的泛型类型一致

class A<T> extends B<T>{}

2、子类不是泛型的情况,父类要明确泛型的数据类型

class A extends B<String>{}

2.2.2 泛型接口

泛型接口语法:

 interface 接口名称 <泛型标识,泛型标识, ...>{ 泛型标识 方法名(); ... }

1、实现类也是泛型类,泛型类型要和接口一致

 interface B <T,V> {} class A<T,V> implements B<T,V>{}

2、实现类不是泛型类,接口要指定具体的类型

 class B implements B<String,Integer>{}

2.2.3 泛型方法

泛型方法语法

修饰符 <T,E,...> 返回值类型 方法名 (形参列表){
  方法体;
}

泛型方法特征:

只有声明了

的方法才是泛型方法,仅仅传参使用了泛型的,不是泛型方法

和泛型类一样,也可以使用K、V、T、E等等参数表示泛型

public class GenericsMethodExample {
    public static void main(String[] args) {
        A0 a0 = new A0();
        a0.test0("hello");
        A1<Integer> a1 = new A1<>();
        a1.test("hi", "hello");
        A1.test1("hello world");
    }
    static class A0 {
        public <T, E> T test0(T t) {
            System.out.println("t = " + t);
            return null;
        }
    }
    static class A1<K> {
        /**
         * 泛型方法的类型和泛型类一致时,以具体方法的为准
         * @param k
         * @param t
         */
        public <T, K> T test(T t , K k) {
            System.out.println("t = " + t);
            System.out.println("k = " + k);
            return null;
        }
        /**
         * static方法要使用泛型,必须指定为泛型方法加上{@code <K>}
         * @param k
         */
        public static <K> void test1(K k) {

        }
    }
}


归纳:

泛型方法的类型和泛型类一致时,以具体方法的为准

static方法要使用泛型,必须指定为泛型方法加上

2.4 泛型通配符

类型通配符一般是使用?代替具体的类型实参,所以类型通配符是类型实参,而不是类型形参

public class GenericsWildcardExample {

    public static void main(String[] args) {
        A<String> a0 = new A<>();
        show(a0);

        A<Integer> a1 = new A<>();
        show(a1);

    }

    public static void show(A<?> a){
        System.out.println(a.getClass());
    }

    static class A<T> {

    }
}


2.7 类型通配符上限

语法:

 类/接口 <? extends 实参类型>

要求改泛型类型只能是实参类型或者实参的子类类型

public class GenericsWildcardExample {
    public static void main(String[] args) {
        A<A1> a11 = new A<>();
        A<A2> a12 = new A<>();
        A<A3> a13 = new A<>();
        // 只能使用A2及其子类
        show0(a12);
        show0(a13);
    }
    public static void show0(A<? extends A2> a) {}
    static class A<T> {
    }
    static class A1{}
    static class A2 extends A1{}
    static class A3 extends A2{}
}


8、类型通配符下限

语法

 类/接口 <? super 实参类型>

要求改泛型类型只能是实参类型或者实参的父类类型

public class GenericsWildcardExample {
    public static void main(String[] args) {
        A<A1> a11 = new A<>();
        A<A2> a12 = new A<>();
        A<A3> a13 = new A<>();
        // 只能使用A2及其子类
        show0(a12);
        show0(a13);
    }
    public static void show0(A<? extends A2> a) {}
    static class A<T> {
    }
    static class A1{}
    static class A2 extends A1{}
    static class A3 extends A2{}
}


2.9 什么是泛型擦除?

泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型有关的信息会被擦除掉,我们称之为 类型擦除

2.9.1 无限制类型擦除


public class GenericsWipeExample {
    public static void main(String[] args) {
        Class<?> cls0 = A.class;
        Field[] fields = cls0.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + ":" + field.getType().getSimpleName());
        }
    }
    static class A <T>{
        T t;
    }
}

打印:

t:Object

2.9.2 有限制类型擦除


public class GenericsWipeExample {
    public static void main(String[] args) {
        Class<?> cls1 = A1.class;
        Field[] fields1 = cls1.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field.getName() + ":" + field.getType().getSimpleName());
        }
    }
    static  class A1<T extends Number> {
        T t;
    }
}

打印:

t:Number

2.9.3 方法类型擦除


public class GenericsWipeExample {
    public static void main(String[] args) {      
        Class<?> cls2 = A2.class;
        Method[] methods = cls2.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
        }
    }
    static class A2<T extends Number> {
        T t;
        public <K extends Number> K test(K k) {
            return null;
        }
    }
}

打印

test:Number

2.9.4 桥接方法


interface MyInter<T>{
  T fun1();
}
class DD implements MyInter<String>{
  @Override
  public String fun1() {
    return null;
 }
}
public class GenericsWipeExample {
    public static void main(String[] args) {
        Class<?> cls3 = A3.class;
        Method[] methodss = cls3.getDeclaredMethods();
        for (Method method : methodss) {
            System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
        }
    }
    interface MyInter<T>{
        T test();
    }
    class A3 implements MyInter<String> {
        @Override
        public String test() {
            return null;
        }
    }
}

打印:

test:Stringtest:Object

2.10 泛型和反射

2.10.1 反射

java反射机制:在程序运行时,能知道任意一个类的所有属性和方法,对于任意一个对象都能调用它的任意一个方法和属性。这种动态的获取信息或动态的调用对象的方法称之为反射。

 Class<?> cls0 = A.class;
Field[] fields = cls0.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName() + ":" + field.getType().getSimpleName());
}


2.10.2 反射总结(下篇文章详解反射)

1.反射的作用:可以通过反射在运行时动态的获取任意类的任意成员变量及任意方法,以及动态的进行访问任意成员变量及任意方法(包括private方法与private对象)当类中存在私有常量,反射也可以进行更改,但如果私有常量在声明时赋值,则无法对其相关的方法产生影响。(因为虚拟机会在编译时将.java文件得到.class文件时,会将静态常量的引用代码都直接更改为静态常量的具体数值)。

2.获取类的变量信息与获取类的方法信息类似。创建Class对象,并分别通过getFields()getMethods()方法可以获取到类的变量与方法。注意这里获取到的只有当前类及其父类的public方法与public的成员变量。

3.反射可以访问私有方法和私有对象。都需要通过privateMethod.setAccessible(true)获取访问权,对应私有方法就使用privateMethod.invoke(对象,形参)方法来调用privateMethod;对应私有对象就使用`privateField.set(对象, 要修改成的值);

4.修改私有常量的方法需要在构造函数中对私有常量赋值才可以,否则则无法对引用私有常量的方法起到作用。

5.由于泛型会被类型擦除,使用反射的type接口,可以来确定信息。同时,使用反射可以构建泛型数组。因为通过Class commpentType = c1.getComponentType();可以获取到数组的元素类型,进而创建相应的数组Object newArray =newInstance(commpentType,length)。只暂时让数组为Object,之后再转换类型num = (int)Arrays.goodcopyOf(num,10)。

2.11 泛型的优点

1 泛型简单易用

2 类型安全 泛型的主要目标是实现java的类型安全。 泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型

3 消除了强制类型转换 使得代码可读性好,减少了很多出错的机会

4 Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

3 常见面试题


1. Java中的泛型是什么 ? 使用泛型的好处是什么?
2. Java的泛型是如何工作的 ? 什么是类型擦除 ?
3. 什么是泛型中的限定通配符和非限定通配符 ?
4. List<? extends T>和List <? super T>之间有什么区别 ?
5. 如何编写一个泛型方法,让它能接受泛型参数并返回泛型类型?
6. Java中如何使用泛型编写带有参数的类?
7. 编写一段泛型程序来实现LRU缓存?
8. 你可以把List<String>传递给一个接受List<Object>参数的方法吗?
9. Array中可以用泛型吗?
10. 如何阻止Java中的类型未检查的警告?
11.Java 泛型类、泛型接口、泛型方法有什么区别?
12. Java 如何优雅的实现元组?
13. java泛型中<?>和<T>有什么区别?


常见出现的问题会在后下一篇的文章讨论,一起学习的朋友可以点点关注,会持续更新,文章有帮助的话可以长按点赞有惊喜!!!文章比较长,大家可以先 收藏转发后看有什么补充可以在下面评论,谢谢大家

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表