博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Generics
阅读量:4695 次
发布时间:2019-06-09

本文共 4230 字,大约阅读时间需要 14 分钟。

Generics
 
 
 
201211260920089089.png
 
 
 
generic
generic /dʒəˈner.ɪk/
adjective 形容词
1 FORMAL shared by, typical of or relating to a whole group of similar things, rather than to any particular thing:
共享,共性
The new range of engines all had a generic problem with their fan blades.
2 MAINLY US describes a product that is not marked with the name of the company that produced it:
一般的
a generic drug
generic
adj 种属的;普遍的=general
【记】源于:gene基因genus(n 生物学种类);genre(n 文学类型;流派)
【区】genetic(adj 遗传的;起源的)"
 
 
反义词:
Specific
 
 
因为Object是所有对象的超类,所以它可以用在处理任意类型的时候作为占位。
 
然而,如果当该用String作为参数的时候,你使用了Object来占位,然后你可以将Object转为String来使用, 那么只有你才知道这个潜规则,Java程序是无法预知这个规则的,那么Java程序就无法帮你做必要的验证,当你不小心使用Integer作为参数的时候而想转换成String的时候就会出错,本质的原因是“信息丢失”了,你无法将这个潜规则反应到程序里面。
 
Generic的出现就是为了弥补这个缺陷!
 
 
type variable 类型变量
<E> -- 表示Element类型,即是对象的一个元素。
<K> -- 表示Key类型 
<V> -- 表示Value类型
<T> -- 表示通用Generic类型
   (这只是惯例而已,你可以任意定义类型变量名)
 
无泛型的代码如下:
Cell strCell = new Cell("Hello");
有泛型的代码如下:
Cell<String> strCell = new Cell<String>("Hello");
你可以认为是没有泛型的代码少了点信息,也可以认为有泛型的代码多了点什么。
既然泛型的存在有一定的作用,那么势必需要多一点信息熵来给起作用的地方去参考。
 
换个角度来说,也是“数据”和“代码”的一次完美转换。本来我们需要重写任意多的代码,这条路走不通,因为违反了“有穷性”,我们无法完全定义出所有可能性的类型,即代码这条路走不通,于是我们改变了数据结构,在数据结构里面加了一个类型变量,这个变量来指代所有的可能性,以不变应万变。
 
 

1. 定义

  • 泛型类不会因为类型参数不同而存在多份
  • 泛型信息在编译后就被擦除,不存在于class文件中
  • 泛型是给编译器提供信息而不是运行时
每一个对泛型类的调用都称为一次 parameterized invocation,即使同一个类的参数化的调用而产生的不同对象,但是本质上是共享同一份代码,是同一个类。
 
因此,对于类方法(Static)和类(Static)字段,都不可以使用泛型参数。
 
 

1.2. 类型参数的界限

<E>作为一般形式,并没有限制E的类型,也就是说E可以接受任意类型,但是现实需求往往是需要现在E在一定范围内
 
<E extends XXXClass>
 
 
1.3 子类型subtyping和通配符wildcards
 
wildcard '?'
List<? extends XXX> list
 
 
static double sum(List<Number> list) {
    double sum = 0.0;
    for (Number n : list)
        sum += n.doubleValue();
    return sum;
}
List<Integer> l = new ArrayList<Integer>();
l.add(1);
l.add(4);
l.add(9);
double sum = sum(l);   // INVALID: won't compile
 
 
 
 

11.5 背后实现原理: 编译时擦除

erasure:因为编译器基本上会将class文件里的所有的泛型类型信息擦除。
  1. 首先,如果是<E>就替换为Object
  2. 如果是< E extends ?>, 就替换为最近的那个?
  3. 如果使用到绑定的非Object方法,就做一个cast
 
类型擦除影响到如下2个关键点:
  1. 包含泛型类型的运行时操作
  2. 方法的重载(同类)和重写{继承}
 

擦除对运行时的影响如下:

  • 无法 new T(), new T[n]
  • =============================== 看不懂

对重载和重写的影响

class Base<T> {
    void m(int x)    {}
    void m(T t)      {}
    void m(String s) {}
    <N extends Number> void m(N n) {}
    void m(SingleLinkQueue<?> q)   {}
}
 
类型擦除后就是如下代码:
 
void m(int x)    {}
void m(Object t) {}
void m(String s) {}
void m(Number n) {}
void m(SingleLinkQueue q) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
泛型:
 
本质上参数化类型,主要是针对集合,因为如果是非集合,那么类型肯定已经确定了!!!
 
 
泛型可以用在类,接口和方法的创建中。
 
 
下面举一个用在泛型类中的例子,因为类生产的对象可以看出是一个字典,而字典就是一个集合!
package com.iu.www.generics;
//泛型的写法,非常的通用,不需要类型转换
public class Gen<T> {
    private T ob;
    public Gen(T ob){
        this.ob = ob;
    }
    public T getOb() {
        return ob;
    }
    public void setOb(T ob) {
        this.ob = ob;
    }
    public void showType(){
        System.out.println("the param is : "+ob.getClass().getName());
    }
    public static void main(String[] args) {
        Gen<String> hello = new Gen<String>("Hello");
        hello.showType();
    }
// 一般的写法, 因为Object没有泛化,所以不清楚Object是什么,所以有强制类型转换的过程,而且这个过程编译器没办法帮助检查,可能会有运行时错误
public class Gen2 { 
        private Object ob;            //定义一个通用类型成员 
        public Gen2(Object ob) { 
                this.ob = ob; 
        } 
        public Object getOb() { 
                return ob; 
        } 
        public void setOb(Object ob) { 
                this.ob = ob; 
        } 
        public void showTyep() { 
                System.out.println("T的实际类型是: " + ob.getClass().getName()); 
        } 
}
// 最搓的写法, 每个类型都要重复写一遍,重复的模板代码
     class GenString {
        private String ob;
        public GenString(String ob){
            this.ob = ob;
        }
        public String getOb() {
            return ob;
        }
        public void setOb(String ob) {
            this.ob = ob;
        }
        public void showType(){
            System.out.println("the param is : "+ob.getClass().getName());
        }
     }
    class GenInteger {
        private Integer ob;
        public GenInteger(int ob){
            this.ob = ob;
        }
        public Integer getOb() {
            return ob;
        }
        public void setOb(int ob) {
            this.ob = ob;
        }
        public void showType(){
            System.out.println("the param is : "+ob.getClass().getName());
        }
    }
}
 
 
 
其中泛型的参数类型一般有如下几种,但是这只是一个建议而已,你可以改成任何你想要的名字。
 E for an element type, 
 K for a key type, 
 V for a value type, 
 T for a general type, 
 and so forth.
 
 
注意如下代码,泛型定义的类型参数不一定是给构造器使用,它可以用在类的任何方法上。
 
package com.iu.www.generics;
public class Map2<K,V> {
    public V get(K key) {
        return null;
    }
    public V put(K key, V value) {
        return null;
    }
    public static void main(String[] args) {
        Map2<String, Integer> stringIntegerMap2 = new Map2<String, Integer>();
        stringIntegerMap2.put("",1);
        Integer integer = stringIntegerMap2.get("");
    }
}

转载于:https://www.cnblogs.com/depeng/archive/2012/11/26/2788511.html

你可能感兴趣的文章
PAT(B) 1014 福尔摩斯的约会(Java)
查看>>
PAT甲级题解-1123. Is It a Complete AVL Tree (30)-AVL树+满二叉树
查看>>
项目开发总结报告(GB8567——88)
查看>>
SSH加固
查看>>
端口扫描base
查看>>
iOS IM开发的一些开源、框架和教程等资料
查看>>
FansUnion:共同写博客计划终究还是“流产”了
查看>>
python 二维字典
查看>>
pip 警告!The default format will switch to columns in the future
查看>>
Arrays类学习笔记
查看>>
实验吧之【天下武功唯快不破】
查看>>
2019-3-25多线程的同步与互斥(互斥锁、条件变量、读写锁、自旋锁、信号量)...
查看>>
win7-64 mysql的安装
查看>>
dcm4chee 修改默认(0002,0013) ImplementationVersionName
查看>>
maven3在eclipse3.4.2中创建java web项目
查看>>
发布时间 sql语句
查看>>
黑马程序员 ExecuteReader执行查询
查看>>
记一些从数学和程序设计中体会到的思想
查看>>
题目1462:两船载物问题
查看>>
POJ 2378 Tree Cutting(树形DP,水)
查看>>