zoukankan      html  css  js  c++  java
  • 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());
            }
    
    }
  • 相关阅读:
    GoldenGate Studio 12.2.1.1发布
    重构-改善既有代码的设计完整笔记系列之8
    重构-改善既有代码的设计完整笔记系列之6、7
    Java多线程开发系列-线程管理
    Java多线程开发系列-线程活性故障
    Java多线程开发系列-线程间协作
    Java多线程开发系列-基础
    了不起的Java-CompletableFuture组合异步编程
    了不起的Java-Optional替代null处理
    了不起的Java-Lambda替代设计模式
  • 原文地址:https://www.cnblogs.com/liu-shijun/p/10894506.html
Copyright © 2011-2022 走看看