Java泛型

1. 概要

  • Java泛型特点是一套代码,多次应用。其中多次特指适用多用参数类型。
  • Java泛型体现在编译阶段,编译过后,泛型类型被擦除,替换为基本Object类型。
  • Java泛型包括类泛型、接口泛型、方法泛型,其中方法泛型包括静态方法泛型(不兼容类上的泛型参数)。
  • 不能创建指定参数类型的泛型数组,但可创建通配符类型的泛型数组。
  • Java反射可以破坏泛型执行。

2. 一套代码,适配多种参数类型

以下是一段简单的代码示例,不再赘述。

public class GenericsDemo<T> {

       public T g_var_a;

       public void set(T data) {
               g_var_a = data;
       }

       public T get() {
               return g_var_a;
       }
       
}

public class GenericsUsingDemo {

       public static void main(String[] args) {
               GenericsDemo<String> g_str = new GenericsDemo<String>();
               g_str.set("test");
               System.out.println(g_str.get());

               GenericsDemo<Integer> g_int = new GenericsDemo<Integer>();
               g_int.set(100);
               System.out.println(g_int.get());

       }

}

3. 编译过后,类型擦除

反编译上文GenericsUsingDemo类,可以查看调用GenericsDemo类的set方法参数类型和get方法返回类型均为java/lang/Object。使用时,调用checkcast指令自动进行类型转换。

public class GenericsUsingDemo {
  public GenericsUsingDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class GenericsDemo
       3: dup
       4: invokespecial #3                  // Method GenericsDemo."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String test
      11: invokevirtual #5                  // Method GenericsDemo.set:(Ljava/lang/Object;)V
      14: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      17: aload_1
      18: invokevirtual #7                  // Method GenericsDemo.get:()Ljava/lang/Object;
      21: checkcast     #8                  // class java/lang/String
      24: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: new           #2                  // class GenericsDemo
      30: dup
      31: invokespecial #3                  // Method GenericsDemo."<init>":()V
      34: astore_2
      35: aload_2
      36: bipush        100
      38: invokestatic  #10                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      41: invokevirtual #5                  // Method GenericsDemo.set:(Ljava/lang/Object;)V
      44: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      47: aload_2
      48: invokevirtual #7                  // Method GenericsDemo.get:()Ljava/lang/Object;
      51: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      54: return
}

4. 泛型类型

如前文所述,Java泛型包括类泛型、接口泛型、方法泛型。其中类泛型、接口泛型不再相赘述,本节重点探讨下方法泛型。

4.1 普通泛型方法

普通泛型方法定义需要在方法返回类型前指定泛型标志<T>,且可直接引用泛型类上的泛型标志。

public class GenericsDemo<T> {

        public T g_var_a;
        public T g_var_b;

        public void set(T data) {
                g_var_a = data;
        }

        public T get() {
                return g_var_a;
        }

        public <M> void g_method_set(M m_data, T t_data) {
                g_var_a = t_data;
                System.out.println(g_var_a);
                System.out.println(m_data);

        }

}

4.2 静态泛型方法

静态泛型方法不能访问泛型类上的泛型标志。

public class GenericsDemo<T> {

        public T g_var_a;
        public T g_var_b;

        public void set(T data) {
                g_var_a = data;
        }

        public T get() {
                return g_var_a;
        }

        public static <M> void g_method_set(M m_data, T t_data) {
                g_var_a = t_data;
                System.out.println(g_var_a);
                System.out.println(m_data);

        }

}
[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsDemo.java
GenericsDemo.java:17: error: non-static type variable T cannot be referenced from a static context
        public static <M> void g_method_set(M m_data, T t_data) {
                                                      ^
GenericsDemo.java:18: error: non-static variable g_var_a cannot be referenced from a static context
                g_var_a = t_data;
                ^
GenericsDemo.java:19: error: non-static variable g_var_a cannot be referenced from a static context
                System.out.println(g_var_a);
                                   ^
3 errors

4.3 可变参数泛型方法

如果用泛型标志悠久可变参数,则调用该方法,可传入任意类型参数组合。

public class GenericsDemo<T> {

        public T g_var_a;
        public T g_var_b;

        public void set(T data) {
                g_var_a = data;
        }

        public T get() {
                return g_var_a;
        }

        public <M> void g_method_set(M... m_data) {
                for(M m : m_data) {
                        System.out.println(m);
                }

        }

}
public class GenericsUsingDemo {

        public static void main(String[] args) {
                GenericsDemo<String> g_str = new GenericsDemo<String>();
                g_str.g_method_set("test", 1);
        }

}
public class GenericsUsingDemo {

        public static void main(String[] args) {
                GenericsDemo<?> g_obj = new GenericsDemo<Object>();
                g_obj.g_method_set("test", 1);
        }

}
[root@iZbp1fza94gkbdpgoq00xcZ test]# java GenericsUsingDemo
test
1

5. 泛型数组

声明泛型数组须使用通配符标志,不能指定参数类型。具体原因,可以仔细思考下前文反编译中的checkcast指令。

public class GenericsUsingDemo {

        public static void main(String[] args) {
                GenericsDemo<?>[] g_obj = new GenericsDemo<?>[5];
                System.out.println(g_obj.length);
        }

}

另外,虽然可以声明通配符类型数组,但不能声明通配符类型变量。

[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsUsingDemo.java
GenericsUsingDemo.java:7: error: unexpected type
                GenericsDemo<?> g_obj = new GenericsDemo<?>();
                                                        ^
  required: class or interface without bounds
  found:    ?
1 error

6. 泛型上下边界

泛型还可以应用上下边界,语法如下,不再赘述。

  • <? extends T>:是指 “上界通配符(Upper Bounds Wildcards)
  • <? super T> :是指 “下界通配符(Lower Bounds Wildcards)

7. Java反射对泛型的影响

Java反射可以影响泛型的执行阶段。如下面一段代码,可以通过编译阶段,但在执行时会提示Integer类型无法转换为String类型异常。如果不使用反射,如前文示例,直接调用set方法,传递Integer类型参数,会在编译阶段报错。

  1 import java.lang.reflect.*;
  2
  3 public class GenericsUsingDemo {
  4
  5
  6         public static void main(String[] args) throws Exception {
  7                 GenericsDemo<String> g_obj = new GenericsDemo<String>();
  8                 Class<?> clazz = g_obj.getClass();
  9                 Method method = clazz.getMethod("set", Object.class);
 10                 method.invoke(g_obj, 1);
 11                 System.out.println(g_obj.get());
 12
 13
 14         }
 15
 16 }
[root@iZbp1fza94gkbdpgoq00xcZ test]# javac GenericsUsingDemo.java
[root@iZbp1fza94gkbdpgoq00xcZ test]# java GenericsUsingDemo
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at GenericsUsingDemo.main(GenericsUsingDemo.java:11)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值