Java泛型初探

一、泛型概念    

一般的类和方法,只能使用具体的类型,要么是基础类型,要么是自定义的类,接口等。泛型,按字面意思来理解就是泛化的类型。什么是泛化的类型呢,在面向对象里,继承是一种泛化机制,方法可以接受一个基类的参数,那么该基类延伸出来的所有子类都可以传递进来,这可以说是一种泛化,广泛化,通用化。由于Java的单继承和final类的不可继承,这种泛化是有很大限制的。接口呢,进一步扩大化了代码的表达能力,可以多继承。如果方法的参数是一个接口,这种限制就放松了很多,任何实现了该接口的类都可满足该方法。但有时候接口仍然不满足我们对于通用性代码的要求,因为一旦指明了接口,就必须使用这种特定的接口,有时候我们需要的是“某种不具体的类型”,而不是某个类或接口。 你知道它是一种类型,但具体是哪种还不确定,要到实际调用的时候才会确定使用哪种类型。由此,泛型实现了参数化类型的概念,使得代码可以应用于多种类型,具有更广泛的表达能力。

那么到底什么是泛型,给一个比较正式的定义。泛型:即参数化类型。将我们平时用的具体的类型参数化,类似于方法调用的形参,实参。所需要的类型定义的时候是不确定的,参数化的,就像形参一样。实际使用的时候再确定具体的类型,类比实参。

二、为什么使用泛型

举一个被举了无数遍的例子

List list = new ArrayList();
list.add("abc");
list.add("123");
list.add(100);

for( int i = 0; i < list.size(); i++ ){
    String val = (String) list.get(i);
    System.out.println("泛型测试"+ val)
}

在编译期,这完全没有问题,编译器不会报错。list默认可以存入任何Object的子类,所以可以放入两个String类型和一个Integer类型。但运行时程序就会崩溃

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

泛型能够很好的解决这个问题,在编译期就能够发现这样的类型不匹配的问题。

List<String> list = new ArrayList<String>();
list.add("abc");
list.add("123");
list.add(100);  ## compile error

for( int i = 0; i < list.size(); i++ ){
    String val = (String) list.get(i);
    System.out.println("泛型测试"+ val)
}

编译器立刻就能够发现这样的错误。

三、泛型基本用法:泛型类、  泛型接口、 泛型方法

  • 泛型类
    • 泛型类基本语法
      1 class 类名<泛型标识,可以使用任意的泛型标识,例如常用的T, K, V。 标识泛型所指定的类型>2     private 泛型标识 val;
      3

       最简单的一个泛型类

      public class Generic<T> {
          private T val;
          Generic(){
          }
      
          Generic(T val){
              this.val = val;
          }
          
          public T getVal(){
              return val;
          }
      }
      

        

  • 泛型接口
    • 泛型也可以用于接口上,常用用法为类的生成器,可以生成各种不同类型的对象。语法跟泛型类相同
      public interface Generator<T> {
          public T generate();
      }
      
      // 泛型实现类这里又分两种:
      // 1、实现类未传入泛型实参,那么实现类也必须将接口的泛型声明一起加入到类中来。
      // class CustomGenerator implements Generator<T> 编译器会报错
      public class CustomGenerator<T> implements Generator<T>{ private T val; CustomGenerator(T val){ this.val = val; } public T generate(){ return val; } public static void main(String[] args) { CustomGenerator<String> custGeneraotr = new CustomGenerator<String>("213"); System.out.println(custGeneraotr.generate()); } }
      // 2、传入泛型实参时,虽然我们只定义了一个泛型接口,但可以为 T 传入不同的实参形成许多不同的具体类生成器,
      // 例如传入 String 得到 String 的生成器,传入 Intege r得到 Integer 的生成器。
      // 传入泛型实参时,原接口中所有泛型标识都要替换为具体的泛型实参,例如原接口中的 public T generator()
      // 需变为 public String generator();

      public class StringGenerator implements Generator<String>{
      @Override
      public String generate() {
      return "";
      }
      }

      class IntegerGenerator implements Generator<Integer>{
        @Override
      public Integer generator(){
      return 0;
      }
      }
       
  • 泛型方法 

         泛型也可以用于单独的方法上,并不要求所在的类上泛型类。泛型类和泛型方法之间并没有必然的联系。但是有一个原则: 无论何时,只要你能做到,你就应该尽量使用泛型方法

也就是说,如果使用泛型方法能够替代使用泛型类,你就应该只使用泛型方法,这样事情更简单明白。 

另外,static 静态方法无法访问泛型类的类型参数,如果静态方法要使用泛型参数,那么就必须声明为泛型方法。

 

基本语法
public <泛型标识> 返回值 方法名(。。。){
      ... 方法体
}

 具体示例:

import java.util.HashMap;
import java.util.Map;

public class GenericTest{
        // 这是一个泛型类
        class Generics<T> {
                private T key;

                Generics(T val) {
                        this.key = key;
                }

                /**
                 * 首先这不是一个泛型方法,这只是泛型类里的一个普通成员方法,
                 * 只不过它的返回值上泛型类声明过的泛型参数
                 *
                 * @return
                 */
                public T getKey() {
                        return key;
                }
        }

                /**
                 * 这也不是一个泛型方法,它只不过接受一个泛型类做形参而已
                 * @param generics
                 */
                public void showKey(Generics<Integer> generics){
                        System.out.println(generics.getKey());
                }

                /**
                 * 这才是一个泛型方法 public 与返回值之间都 <T> 必不可少,这表明这是一个泛型方法。
                 * 当然方法的泛型参数可以有多个,也可以跟类泛型参数不同,方法里我可以用E,也可以用T
                 * 例如 public <k,V> showKeyValue(){   ...   }
                 * @param <E>
                 * @return
                 */
                public <E> E showKeyValue(Generics<E> container){
                        return container.getKey();
                }

        //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
        public void showKeyValue2(Generics<?> obj){
               System.out.println("泛型测试 key value is " + obj.getKey().toString());
        }

}

 

点赞