Java中的泛型/范型

维基百科中关于Java泛型的描述

Java 泛型的参数只可以代表类,不能代表个别对象。由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。Java编译程序在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。

由于运行时会消除泛型的对象实例类型信息等缺陷经常被人诟病,Java及JVM的开发方面也尝试解决这个问题,例如Java通过在生成字节码时添加类型推导辅助信息,从而可以通过反射接口获得部分泛型信息。通过改进泛型在JVM的实现,使其支持基本值类型泛型和直接获得泛型信息等。

Java允许对个别泛型的类型参数进行约束,包括以下两种形式(假设T是泛型的类型参数,C是一般类、泛类,或是泛型的类型参数):

  • T实现接口I
  • TC,或继承自C

泛型Since jdk1.5

摘自《Java代码与架构完美优化》
泛型的本质是参数化类型,所操作的数据类型被指定为一个参数。
Java中的泛型在编译器中实现,而不是在虚拟机中实现的,虚拟机对于泛型是一无所知的。因此,编译器一定要把泛型类修改为普通类,才能够在虚拟机中运行。Java中把这种技术称为擦除,泛型代码经过擦除后变成原生类型。

源代码>>>泛型>>>编译器>>>字节码(*.class文件)>>>JVM(类装载器,字节码校验器,解释器)>>>操作系统平台

使用泛型的优势

类型安全以及不需要进行类型转换

类型安全性:我们只能在泛型中只保存一种类型的对象。 它不允许存储其他对象。因此,也不再需要进行类型转换。下面给出使用泛型和不使用泛型的区别。

List list = new ArrayList();  
list.add("Hello Generics");  
String s = (String) list.get(0);  //需要类型转换
List<String> list = new ArrayList<String>();  
list.add("Hello Generics");  
String s = list.get(0);   //不需要类型转换

类型检查从运行时挪到编译时

编译时检查:在编译时检查,所以运行时不会出现问题。 良好的编程策略表明,在编译时处理这个问题要比运行时好得多。

使用泛型的注意事项

  • 在static方法中不可以使用泛型,泛型变量也不可以使用static关键字来修饰
  • 泛型常用符号的含义-T(Type)、K(Key)、V(Value)、E(Element),N(Number)尽管其它形式也可以作为变量的符号,但是我们习惯使用这几种符号。
  • 泛型只适用于对象,基本类型不适用,但是可以使用基本类型的包装类来实现,比如:
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);  //自动将1转换成Integer
intList.add(2);
  • 开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List<Object>作为形式参数,那么如果尝试将一个List<String>的对象作为实际参数传进去,却发现无法通过编译。虽然从直觉上来说,Object是String的父类,这种类型转换应该是合理的。但是实际上这会产生隐含的类型转换问题,因此编译器直接就禁止这样的行为。http://www.infoq.com/cn/articles/cf-java-generics

泛型使用举例

创建参数化类型-泛型类

//simple
class MyGen<T> {
    T obj;

    void add(T obj) {
        this.obj = obj;
    }

    T get() {
        return obj;
    }
}

public class Generics {

    public static void main(String args[]) {
        MyGen<Integer> m = new MyGen<Integer>();
        m.add(2);
        //m.add("test");   //Compile time error
        System.out.println(m.get());
    }
}

//complex from agile java
import java.util.*;
public class MultiHashMap <K,V> {
   private Map<K, List<V>> map = new HashMap<K, List<V>>();

   public static <K extends Comparable<K>,V> List<K>
         sortedKeys(MultiHashMap<K, V> map) {
      List<K> keys = new ArrayList<K>();
      keys.addAll(map.keys());
      Collections.sort(keys);
      return keys;
   }

   public Set<K> keys() {
      return map.keySet();
   }

   public int size() {
      return map.size();
   }

   public void put(K key, V value) {
      List<V> values = map.get(key);
      if (values == null) {
         values = new ArrayList<V>();
         map.put(key, values);
      }
      values.add(value);
   }

   public List<V> get(K key) {
      return map.get(key);
   }

   protected Set<Map.Entry<K, List<V>>> entrySet() {
      return map.entrySet();
   }

   public interface Filter<T> {
      boolean apply(T item);
   }

   public static <K,V> void filter(final MultiHashMap<K, ? super V> target,
                                   final MultiHashMap<K, V> source,
                                   final Filter<? super V> filter) {
      for (K key : source.keys()) {
         final List<V> values = source.get(key);
         for (V value : values)
            if (filter.apply(value))
               target.put(key, value);
      }
   }
}

泛型方法

public class Generics {

    public static < E > void printArray(E[] elements) {  
        for ( E element : elements){          
            System.out.println(element );  
         }  
         System.out.println();  
    }  
    public static void main( String args[] ) {  
        Integer[] intArray = {6, 66, 666};  
        String[] stringArray = { "6","66","666" };  
  
        System.out.println( "Printing Integer Array" );  
        printArray( intArray  );   
  
       System.out.println( "Printing String Array" );  
        printArray( stringArray );   
    }   
}

上限

每个类型参数都有一个缺省为Object的上限,你可以将类型参数限制为不同的上限。这里需要使用extends关键字来指定某个类型参数的上限。

//from agile java
import java.util.*;
public class EventMap<K extends Date,V>
   extends MultiHashMap<K,V> {
   public List<V> getPastEvents() {
      List<V> events = new ArrayList<V>();
      for (Map.Entry<K,List<V>> entry: entrySet()) {
         K date = entry.getKey();
         if (hasPassed(date))
            events.addAll(entry.getValue());
      }
      return events;
   }

   private boolean hasPassed(K date) {
      Calendar when = new GregorianCalendar();
      when.setTime(date);
      Calendar today = new GregorianCalendar();
      if (when.get(Calendar.YEAR) != today.get(Calendar.YEAR))
         return when.get(Calendar.YEAR) < today.get(Calendar.YEAR);
      return when.get(Calendar.DAY_OF_YEAR) <
         today.get(Calendar.DAY_OF_YEAR);
   }
}

通配符wildcard

java允许使用一个通配符?来表示任意可能的类型,此外你可以使用extends子句限制通配符的上限。

import java.util.ArrayList;
import java.util.List;

abstract class Shape {
    abstract void draw();
}

class Rectangle extends Shape {
    void draw() {
        System.out.println("drawing rectangle");
    }
}

class Circle extends Shape {
    void draw() {
        System.out.println("drawing circle");
    }
}

public class Generics {
    // creating a method that accepts only child class of Shape
    public static void drawShapes(List<? extends Shape> lists) {
        for (Shape s : lists) {
            s.draw(); // calling method of Shape class by child class instance
        }
    }

    public static void main(String args[]) {
        List<Rectangle> list1 = new ArrayList<Rectangle>();
        list1.add(new Rectangle());

        List<Circle> list2 = new ArrayList<Circle>();
        list2.add(new Circle());
        list2.add(new Circle());

        drawShapes(list1);
        drawShapes(list2);
    }
}

demo来源

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

推荐阅读更多精彩内容