【泛型】跟着 Oracle java doc 学习<1>——泛型

1. 泛型 (Generics)介绍  详情参看 Oracle 官方文档

      泛型 使更多的 bug 在 编译时期就可以被发现 , 为代码增加稳定性 。

      泛型带来的好处 :

                   ★ 在编译时期 强类型检查

                   ★ 转换的减少

                   ★ 使程序实现了 泛型算法 ,可以自定义,更容易使用,类型安全的,并且更容易读

                 

       1.1.    泛型 类型   (Gerneric Types)

                         泛型类型 是 一个通过类型参数化的 泛型 类或者接口 。

                         泛型 类 的定义形式如下 : 类型参数 被 <> 包围 ,紧跟在 类名的后面 ,声明了形参(type parameters 也被称为类型变量 type variables)为 T1,T2...Tn ,类型变量可以是指定的任意非原始类型  ;同样适用于接口

class name<T1, T2, ..., Tn> { /* ... */ }


非泛型和 泛型 栗子  :

非泛型 Box:

/**
 * Created by xlch on 2016/7/21.
 */
public class Box {
    private Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    @Test
    public void test(){
        Box box = new Box();
        box.setObject(2);   //可以传任何类型的值
        box.setObject("333");
    }
}


泛型类 Box :

/**
 * Created by xlch on 2016/7/21.
 */
public class Box<T> {  //泛型类型声明

    // T 代表 类型参数
    private T t;   

    public void set(T t){
        this.t = t;
    }

    public T get(){
        return t;
    }

    @Test
    public void test(){
        Box<Integer> box = new Box<>();  //  JAVA SE 7 的钻石语法:可以省略后面 <> 中的类型参数
        box.set(222);  // 只能传递 Integer 类型的值,否则编译期就会报错
    }
}


          按照惯例 , 类型参数 的命名规则是  : 一个大写字母 ,最常用的 类型参数名称如下 :

                   ★  E   - Element

                   ★  K   - Key

                   ★  T   - Type

                   ★  N   - Number

                   ★  V   -Value

                   ★  S ,U,V   - 2nd ,3trd,4th


         调用和实例化泛型类型

                       调用泛型类型时 ,必须执行泛型类型调用 ,即使用实际的类或者接口代替 类型参数(如 T) :参数化类型(parameterized type)

Box<Integer> integerBox; //和普通的变量声明一样,声明了一个持有 "Integer"引用的 integerBox 变量
                         实例化泛型类 :

Box<Integer> integerBox = new Box<Integer>();
Box<Integer> integerBox = new Box<>(); //java SE 7 的 钻石语法 (Diamond)



                 ·type Parameter 和 Type Argument 术语 :这两个术语的意思是不相同的 。 提供 type arguments 的目的是为了创建参数化的类型。因此,Foo<T> 的 T 属于 type parameter , Foo<String> 的 String 属于 type argument .有点形参实参的感觉。


            可以定义单个类型参数 ,也可以定义多个类型参数 (如JAVA SE 中集合的定义)


        1.2.   原始类型 (Raw Type) :在调用或者实例化泛型类型时没有类型参数 的类或者接口

 栗子 : 应该避免使用 原始类型

/**
 * Created by xlch on 2016/7/21.
 */
public class Foo<T> {

    public void set(T t){
        System.out.println(t);
    }

    @Test
    public void test(){
        Foo<Integer> foo = new Foo<>();
        foo.set(4);

        Foo foo1 = new Foo(); // 这个 Foo 便是 Foo<Integer> 的 原始类型 ,raw type
        foo1.set("333");  // ok
        foo1.set(33);  //  ok
        
        Foo foo2 = foo;  // 会有警告
        foo2.set("22"); // Unchecked call set...的警告
        Foo<Integer> foo3 = foo1; //会有警告
 }
}

       1.3 .  泛型方法 (Generic Method)

             利用类型参数 定义的方法 ,类型参数只能该方法可以使用 ,静态和非静态 泛型以及 泛型构造函数都是可以的 。

语法要求:

           ★ 括号内使用类型参数

           ★ 方法返回类型之前使用类型参数  ,而对于静态的泛型方法 , 类型参数必须出现在方法的返回类型之前 ,static 关键字之后  ;此处的 类型参数可以使用类型的限制 ,extends  or  super

栗子 :

泛型类 :

/**
 * Created by xlch on 2016/7/21.
 */
public class Pair<K,V> {
    private K k;  //类型参数 type parameter
    private V v;  //类型参数

    public Pair(K k, V v) {
        this.k = k;
        this.v = v;
    }

    public K getK() {
        return k;
    }

    public void setK(K k) {
        this.k = k;
    }

    public V getV() {
        return v;
    }

    public void setV(V v) {
        this.v = v;
    }
}

静态方法 util 类

/**
 * Created by xlch on 2016/7/21.
 */
public class Util {

     // 静态泛型方法
    public static <K,V> boolean compare(Pair<K,V> p1,Pair <K,V> p2){
        return p1.getK().equals(p2.getK()) && p1.getV().equals(p2.getV());
    }


    @Test
    public void test(){
        Pair<Integer, String> p1 = new Pair<>(1, "apple");
        Pair<Integer, String> p2 = new Pair<>(1, "apple");
        boolean same = Util.<Integer, String>compare(p1, p2);
        boolean equal = Util.compare(p1, p2);                   //可省略 ,类型推断  type inference
        System.out.println(same);
        System.out.println(equal);
    }
}


               1.4.  受限的类型参数  :即限制类型参数

  

                            即限制 方法参数的传入类型 ,这里使用 extends 关键字,但是这里的 extends 和 类或者 接口的继承是不同的 。

/**
 * Created by xlch on 2016/7/22.
 */
public class Box<T> {

    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    public <U extends Number>void inspect(U u){  // extends 类型参数限制 ,只能传入 Number的实例 或者 Number 的子类实例
        System.out.println(u);
    }

    @Test
    public void test(){
        Box<Integer> box = new Box<>();
        box.inspect("hi");//这时候会报错 ,String 类型不能作为传入的参数类型
    }
}

                多个限制的使用语法 :

                        使用  & 连接 ,但是如果限制中有一个是类,则必须第一个被指定,其他接口跟在后面,否则报错 。 形如 :

<T extends B1 & B2 & B3>


栗子 :

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }

class D <T extends A & B & C> { /* ... */ }  // 类必须放在第一个 ,后面放接口,否则报错

class D <T extends B & A & C> { /* ... */ }  // compile-time error



      1.5  泛型 , 继承 ,子类 

Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger;   // OK


public void someMethod(Number n) { /* ... */ }

someMethod(new Integer(10));   // OK
someMethod(new Double(10.1));   // OK


Box<Number> box = new Box<Number>();
box.add(new Integer(10));   // OK
box.add(new Double(10.1));  // OK


public void boxTest(Box<Number> n) { /* ... */ }
boxTest(Box<Number> nBox); //ok
boxTest(Box<Integer> iBox);  // wrong

        关系图如图 :

                                                                    

 泛型类和子类 :

                                          


       1.6  类型推断 (Type inference)


                       类型推断和 泛型方法 :  例如静态泛型方法 在调用时

                        

BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes); //完全写法

BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);  //可省略 ,java 编译时期自动类型推断 类型为 Integer

                      类型推断和  实例化泛型类  : java SE 7 推出的钻石语法

Map<String, List<String>> myMap = new HashMap<String, List<String>>();
Map<String, List<String>> myMap = new HashMap<>();  //钻石语法
Map<String, List<String>> myMap = new HashMap();  // raw types ,会有警告,不建议这样使用

                      类型推断 和  泛型/非泛型的泛型构造函数

class MyClass<X> {
  <T> MyClass(T t) {
    // ...
  }
}

new MyClass<Integer>("")

                       目标类型

如 Collections 有以下 静态泛型方法

static <T> List<T> emptyList();

调用:

List<String> listOne = Collections.emptyList(); // 则实例 List<String> 为 target type  
List<String> listOne = Collections.<String>emptyList();  // 完全写法
但是 ,如果 有这样的方法

void processStringList(List<String> stringList) {
    // process stringList
}
processStringList(Collections.emptyList()); //List<Object> cannot be converted to List<String>   java SE8 可以正常编译
processStringList(Collections.<String>emptyList()); //java SE 7及 之前的 JDK 版本 必须指定类型参数的值 




2. 通配符 (Wildcards )

              ?  被称为通配符 ,代表一种未知的类型(Type) ,可以被用在各种情形 :  参数 、字段 、本地变量 ,有时候也会作为类型返回 。但是 ,通配符不会使用在下列的情况 : 泛型方法的类型参数 、泛型类实例的创建、或者父类型 。


          2.1 上限通配符

                      形如   <? extends A>   A 类型 或者 A 类型的子类型   

  栗子如下 :

public static double sumOfList(List<? extends Number> list) { // 只能Number 类型的实例或者 Number 的子类的实例
//...
}

            2.2  没有受限的通配符  :形如  :   List<?>

                    如下两个场景可能会使用 :

                       ★    如果写 一个方法 ,该方法可以使用提供的 Object 类型

                       ★   泛型 类中 使用的代码不依赖于 类型参数 ,如 list.size(), 即 Class<T> 不依赖于 T

栗子 :

/**
 * Created by xlch on 2016/7/22.
 */
public class Upper {

    public static void out(List<Object> list){
        for (Object object:list){
            System.out.println(object);
        }
    }

    public static void print(List<?> list){
        for (Object object:list){
            System.out.println(object);
        }
    }

    @Test
    public void test(){
        List<Integer> list = Arrays.asList(1,2,3);
        out(list);                               //编译期会报错, 必须是List<Object>
        print(list);                             //编译期不会报错
    }
}

                    2.3   下限的通配符

                                       形如  <? super A>   : A 类型 或者  A  类型 的父类    

                                           <? extends A>   :  A 类型 或者  A  类型 的子类

           

                    2.4  通配符和子类的关系  (具有协变性)


如下图 : 虽然 Integer 是 Number 的子类 ,但是 List<Integer> 和 List<Number> 是不相关的,即泛型 他们公共父类 是  List<?>

                                                

        List<String> strings = new ArrayList();
        List<?> wildCards = new ArrayList<>();
        List<Object> objects = new ArrayList<>();
      
       wildCards = strings;  // 通配符 ok
        objects = strings;    // 泛型   报错 Incompatible types

                     2.5  通配符匹配 和 帮助方法

小栗子 :

    void foo(List<?> i) {
//        i.set(0, i.get(0));  // 报错
        helper(i);
    }

    private <T> void helper(List<T> t){
        t.set(0,t.get(0));
    }



3 类型 擦除 (Type)

          为了实现 泛型 ,Java 编译器 运用了 类型擦除  :

                ★ 使用 界限类或者 Object(如果 类型参数没有界限) 替换 泛型类中所有的 类型参数 。 因此可以看作是普通的 类 / 接口 / 方法

                ★ 必要时 插入类型的强制转换 以保证类型的安全

               ★  在扩展的泛型 类中 生成桥接方法以保存多态性

         类型擦除 保证 为了 参数化 类型没有 新的类创建 ,因此泛型不会产生运行时开销 。


         3.1 泛型类的擦除

栗子 :

泛型类

public class Node<T> {

    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) }
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}
擦除后的类 (没有界限,所以用 Object 替换类型参数):
public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}

             3.2 泛型方法的擦除

栗子:

public static <T> int count(T[] anArray, T elem) {
    int cnt = 0;
    for (T e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}

擦除后:

public static int count(Object[] anArray, Object elem) {
    int cnt = 0;
    for (Object e : anArray)
        if (e.equals(elem))
            ++cnt;
        return cnt;
}



使用 泛型可能会出先一些烦人的警告,此时可以添加注解消除

@SuppressWarnings(value = {"unchecked"}) // 可添加多个



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值