泛型

泛型实现了参数化类型的概念,使代码应用于 多种类型。参数化类型,顾名思义就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

  • 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();
    }
}

当你创建Holder时,必须指明想持有什么类型的对象,将其置于尖括号内,那么就只能在Holder中存入该类型的对象了,并且在你从Holder中取出他持有的对象时,自动的就是正确的类型了。这就是java泛型的核心概念:告诉编译器想使用什么类型,然后编译器帮你处理一切的细节

  • 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()的返回类型是参数化 的T,接口使用泛型与类使用泛型没啥区别

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();
    }
}

一个实现类,实现Generator<Coffee>接口,能够随机生成不同类型的Coffee对象
参数化 的Generator接口确保next()返回值是参数的类型,CoffeeIterator同时还实现了Iterable接口,所有他可以在循环中使用,这就是第二个构造器 的应用

  • 4.泛型斐波那契的应用
 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()+" "); 
        }
    }
}

这个例子引出了泛型的一个局限性:基本类型无法作为泛型参数,所以Int不行,使用Integer,但java提供了自动打包和自动拆包的功能,可以在基本类型与包装类型之间转换

  • 5.泛型方法
    泛型方法适当方法可以独立于类而产生变化
    要定义泛型方法,只需将泛型参数列表置于返回值之前
 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(" ")));
    }
}

注意:当使用泛型类时,必须在创建对象的时候指定类型参数的值,而是用泛型方法的时候,不必指明参数的类型,因为编译器会帮我们找出具体的类型,这称为类型参数判断

  • 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);
    }
}

这个类提供了一个基本的实现,用以生成某个类的对象。这个类必须具备两个条件:(1)它必须声明为public (2)必须具有默认的构造器

  • 7.匿名内部类
    泛型还可以用于内部类以及匿名内部类
 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的构造器,这可以强制你必须使用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()));
    }
}

因此残酷的现实是:在泛型代码内部,无法获得任何有关泛型参数的信息
java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你惟一知道的就是你在使用一个对象。因此,List<String>和List<Integer>在运行时事实上都是相同的类型

  • 9.擦除的补偿
    擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都无法工作。
 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()));
    }
}

有时必须通过引入类型标签来对擦除进行补偿,这意味着你要显示的传递你的类型Class对象,以便你在类型表达式中用到它

  • 10.泛型数组
    可以使用带范型参数值的类声明数组,却不可有创建数组
 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) {
        
    }
}

类型标记Class<T>被传递到构造器中,以便从擦除中恢复,使得我们可以创建需要的实际类型的数组

  • 11.泛型问题
    1.任何基本类型都不能作为类型参数
    2.实现参数化接口
 一个类不能实现同一个泛型接口的两种变体,由于擦除的原因这两个变体会成为相同的接口

3.转型和警告
使用带有泛型类型参数的转型或instanceof不会有任何效果
4.重载
由于擦除的原因,重载将产生相同的类型签名
于此不同的是当被擦除的参数不能产生惟一的参数列表时,必须提供明显有区别的方法名

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容

  • 2.简单泛型 -********Java泛型的核心概念:告诉编译器想使用什么类型, 然后编译器帮你处理一切细节 2...
    CodingHou阅读 387评论 0 0
  • 本文大量参考Thinking in java(解析,填充)。 定义:多态算是一种泛化机制,解决了一部分可以应用于多...
    谷歌清洁工阅读 453评论 0 2
  • 引言:泛型一直是困扰自己的一个难题,但是泛型有时一个面试时老生常谈的问题;今天作者就通过查阅相关资料简单谈谈自己对...
    cp_insist阅读 1,830评论 0 4
  • 第8章 泛型 通常情况的类和函数,我们只需要使用具体的类型即可:要么是基本类型,要么是自定义的类。但是在集合类的场...
    光剑书架上的书阅读 2,143评论 6 10
  • server 端 client端 result:80端口获取到了81端口的数据(跨域访问) jsonp原理 可以无...
    Dev_yang7阅读 244评论 0 0