Java

Java中的泛型/范型

2018-01-06  本文已影响14人  Ricky_Zuo

维基百科中关于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);   //不需要类型转换

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

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

使用泛型的注意事项

List<Integer> intList = new ArrayList<Integer>();
intList.add(1);  //自动将1转换成Integer
intList.add(2);

泛型使用举例

创建参数化类型-泛型类

//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来源

上一篇下一篇

猜你喜欢

热点阅读