泛型

泛型實現(xiàn)了參數(shù)化類型的概念,使代碼應(yīng)用于 多種類型。參數(shù)化類型,顧名思義就是將類型由原來的具體的類型參數(shù)化,類似于方法中的變量參數(shù),此時類型也定義成參數(shù)形式(可以稱之為類型形參),然后在使用/調(diào)用時傳入具體的類型(類型實參)。

  • 1 簡單泛型
class Automobile{
    public void print(){
        System.out.println("print()");
    }
}
public class Holder<T> {
    private T t;
    public Holder(T t){
        this.t = t;
    }
    public T getT(){
        return t;
    }
    public static void main(String[] args) {
        Holder<Automobile> holder = new Holder<Automobile>(new Automobile());
        holder.getT().print();
    }
}

當(dāng)你創(chuàng)建Holder時,必須指明想持有什么類型的對象,將其置于尖括號內(nèi),那么就只能在Holder中存入該類型的對象了,并且在你從Holder中取出他持有的對象時,自動的就是正確的類型了。這就是java泛型的核心概念:告訴編譯器想使用什么類型,然后編譯器幫你處理一切的細節(jié)

  • 2.一個堆棧類
 public class LinkedList<T> {
    private static class Node<U>{
        U item;
        Node<U> next;
        Node(){
            item = null;
            next = null;
        }
        Node(U item, Node<U> next){
            this.item = item;
            this.next = next;
        }
        boolean end(){return item == null & next == null;}
    }
    private Node<T> top = new Node<T>();
    public void push(T item){
        top = new Node<T>(item, top);
    }
    public T pop(){
        T result = top.item;
        if(!top.end()){
            top = top.next;
        }
        return result;
    }
    public static void main(String[] args) {
        LinkedList<String> lss = new LinkedList<String>();
        for(String str : "zhou li bin".split(" ")){
            lss.push(str);
        }
        String ss;
        while((ss = lss.pop())!=null){
            System.out.println(ss);
        }
    }
}
  • 3.自定義泛型接口
    泛型 也可以用作與接口
 public interface Generator<T> {
    T next();
}

方法next()的返回類型是參數(shù)化 的T,接口使用泛型與類使用泛型沒啥區(qū)別

import java.util.Iterator;
import java.util.Random;
class Coffee{
    private static long counter = 0;
    private final long id = counter++;
    public String toString(){
        return this.getClass().getSimpleName()+" "+ id;
    }
}
class Latte extends Coffee{}
class Mocha extends Coffee{}
class Cappuccino extends Coffee{}
class Breve extends Coffee{}
public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee>{
    private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,Breve.class};
    private static Random rand = new Random(47);
    private int size = 0;
    public CoffeeGenerator(){} 
    public CoffeeGenerator(int sz){
        this.size = sz;
    }
    public static void main(String[] args) {
        CoffeeGenerator co = new CoffeeGenerator();
        for(int i=0;i<4;i++){
            System.out.println(co.next());
        }
        for(Coffee c : new CoffeeGenerator(4)){
            System.out.println(c);
        }
    }
    @Override
    public Coffee next() {
        try{
            return (Coffee) types[rand.nextInt(types.length)].newInstance();
        }catch(Exception ex){
            throw new RuntimeException();
        }
    }
    class CoffeeIterator implements Iterator<Coffee>{
        int count = size;
        @Override
        public boolean hasNext() {
            // TODO Auto-generated method stub
            return count > 0;
        }
        @Override
        public Coffee next() {
            // TODO Auto-generated method stub
            count --;
            return CoffeeGenerator.this.next();
        }
        @Override
        public void remove() {
        }
    }
    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }
}

一個實現(xiàn)類,實現(xiàn)Generator<Coffee>接口,能夠隨機生成不同類型的Coffee對象
參數(shù)化 的Generator接口確保next()返回值是參數(shù)的類型,CoffeeIterator同時還實現(xiàn)了Iterable接口,所有他可以在循環(huán)中使用,這就是第二個構(gòu)造器 的應(yīng)用

  • 4.泛型斐波那契的應(yīng)用
 public class Fibonacci implements Generator<Integer>{
    private int count = 0;
    public Fibonacci(int n){
        this.count = n;
    }
    public Fibonacci(){}
    @Override
    public Integer next() {
        return fib(count++);
    }
    public int fib(int n){
        if(n < 2) return 1;
        return fib(n-2)+fib(n-1);
    }
    public static void main(String[] args) {
        Fibonacci fib = new Fibonacci();
        for(int i=0;i<18;i++){
            System.out.print(fib.next()+" "); 
        }
    }
}

這個例子引出了泛型的一個局限性:基本類型無法作為泛型參數(shù),所以Int不行,使用Integer,但java提供了自動打包和自動拆包的功能,可以在基本類型與包裝類型之間轉(zhuǎn)換

  • 5.泛型方法
    泛型方法適當(dāng)方法可以獨立于類而產(chǎn)生變化
    要定義泛型方法,只需將泛型參數(shù)列表置于返回值之前
 import java.util.Arrays;
public class GenericMethods {
    public <T> void f(T t){
        System.out.println(t.getClass().getSimpleName());
    }
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        gm.f("hello");
        gm.f(10);
        gm.f(Arrays.asList("zhou li bin".split(" ")));
    }
}

注意:當(dāng)使用泛型類時,必須在創(chuàng)建對象的時候指定類型參數(shù)的值,而是用泛型方法的時候,不必指明參數(shù)的類型,因為編譯器會幫我們找出具體的類型,這稱為類型參數(shù)判斷

  • 6.一個通用的Generator
 public interface Generator<T> {
    T next();
}
 public class BasicGenerator<T> implements Generator<T>{
    private Class<T> typpe;
    public BasicGenerator(Class<T> type){
        this.typpe = type;
    }
    @Override
    public T next() {
        try {
            return typpe.newInstance();
        } catch (Exception e) {
            throw new RuntimeException();
        } 
    }
    public static <T> Generator<T> create(Class<T> t){
        return new BasicGenerator<>(t);
    }
}

這個類提供了一個基本的實現(xiàn),用以生成某個類的對象。這個類必須具備兩個條件:(1)它必須聲明為public (2)必須具有默認的構(gòu)造器

  • 7.匿名內(nèi)部類
    泛型還可以用于內(nèi)部類以及匿名內(nèi)部類
 public interface Generator<T> {
    T next();
}
 public class Customer {
    private static long counter = 0;
    private final long id = counter++;
    private Customer(){}
    public String toString(){
        return "Customer\t"+id;
    }
    public static Generator<Customer> Generator(){
        return new Generator<Customer>() {
            @Override
            public Customer next() {
                return new Customer();
            }
        };
    }
}

Customer只有private的構(gòu)造器,這可以強制你必須使用Generator對象

  • 8.擦除的神秘之處
    盡管可以聲明ArrayList.class,但是不能聲明ArrayList<Integer>.class
 import java.util.ArrayList;
public class ErasedTypeEquivalence {
    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println(c1 == c2);
    }
}
 import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
class Frob{}
class Fnorkle{}
class Quark<Q>{}
public class LostInformation {
    public static void main(String[] args) {
        ArrayList<Frob> list = new ArrayList<Frob>();
        Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
        System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
    }
}

因此殘酷的現(xiàn)實是:在泛型代碼內(nèi)部,無法獲得任何有關(guān)泛型參數(shù)的信息
java泛型是使用擦除來實現(xiàn)的,這意味著當(dāng)你在使用泛型時,任何具體的類型信息都被擦除了,你惟一知道的就是你在使用一個對象。因此,List<String>和List<Integer>在運行時事實上都是相同的類型

  • 9.擦除的補償
    擦除丟失了在泛型代碼中執(zhí)行某些操作的能力。任何在運行時需要知道確切類型信息的操作都無法工作。
 public class ClassTypeCapture<T> {
    Class<T> type;
    public ClassTypeCapture(Class<T> type){
        this.type = type;
    }
    public boolean f(Object obj){
        return type.isInstance(obj);
    }
    public static void main(String[] args) {
        ClassTypeCapture<Teller> ctt  =new ClassTypeCapture<Teller>(Teller.class);
        System.out.println(ctt.f(new Automobile()));
    }
}

有時必須通過引入類型標(biāo)簽來對擦除進行補償,這意味著你要顯示的傳遞你的類型Class對象,以便你在類型表達式中用到它

  • 10.泛型數(shù)組
    可以使用帶范型參數(shù)值的類聲明數(shù)組,卻不可有創(chuàng)建數(shù)組
 import java.lang.reflect.Array;
public class GenericArray2<T> {
    private T[] array;
    @SuppressWarnings("unchecked")
    public GenericArray2(Class<T> type,int size){
        array = (T[]) Array.newInstance(type, size);
    }
    public void put(int index,T item){
        array[index] = item;
    }
    public T get(int index){
        return array[index];
    }
    public T[] pop(){
        return array;
    }
    public static void main(String[] args) {
        
    }
}

類型標(biāo)記Class<T>被傳遞到構(gòu)造器中,以便從擦除中恢復(fù),使得我們可以創(chuàng)建需要的實際類型的數(shù)組

  • 11.泛型問題
    1.任何基本類型都不能作為類型參數(shù)
    2.實現(xiàn)參數(shù)化接口
 一個類不能實現(xiàn)同一個泛型接口的兩種變體,由于擦除的原因這兩個變體會成為相同的接口

3.轉(zhuǎn)型和警告
使用帶有泛型類型參數(shù)的轉(zhuǎn)型或instanceof不會有任何效果
4.重載
由于擦除的原因,重載將產(chǎn)生相同的類型簽名
于此不同的是當(dāng)被擦除的參數(shù)不能產(chǎn)生惟一的參數(shù)列表時,必須提供明顯有區(qū)別的方法名

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2.簡單泛型 -********Java泛型的核心概念:告訴編譯器想使用什么類型, 然后編譯器幫你處理一切細節(jié) 2...
    CodingHou閱讀 433評論 0 0
  • 本文大量參考Thinking in java(解析,填充)。 定義:多態(tài)算是一種泛化機制,解決了一部分可以應(yīng)用于多...
    谷歌清潔工閱讀 532評論 0 2
  • 引言:泛型一直是困擾自己的一個難題,但是泛型有時一個面試時老生常談的問題;今天作者就通過查閱相關(guān)資料簡單談?wù)勛约簩?..
    cp_insist閱讀 1,954評論 0 4
  • 第8章 泛型 通常情況的類和函數(shù),我們只需要使用具體的類型即可:要么是基本類型,要么是自定義的類。但是在集合類的場...
    光劍書架上的書閱讀 2,201評論 6 10
  • server 端 client端 result:80端口獲取到了81端口的數(shù)據(jù)(跨域訪問) jsonp原理 可以無...
    Dev_yang7閱讀 331評論 0 0

友情鏈接更多精彩內(nèi)容