Java中Generics的使用

1.泛型的意义

泛型是运用在编译时期的技术,泛型即“类型参数化”。

如果没有泛型,比如我们使用ArrayList<Object>并存入String, Integer, 等等类型的对象,然后取出对象并使用object.length,那么Integer类型的对象就会报错了,因为它不能强转成String!

泛型的好处:

  • 泛型的使用让安全问题在编译时就报错而不是运行后抛出异常,这样便于程序员及时准确地发现问题。
    • Generics编译时编译器会按照<类型名>的类型对容器中的元素进行检查,检查不匹配,就编译失败。
  • 泛型的类或接口在取出对象时将不需要再显式向下类型转换,因为存储的时候就是该类型
    • Generics编译器在文件编译通过后自动擦除泛型标识,即编译通过后产生的.class文件中并没有<类型名>这个标识。编译器在擦除泛型后,会自动将类型转换为原定义的"泛型",这样就不必再做向下类型转换了。

Android中的运用:

  • Presenter定义Fragment泛型,这样可以在Presenter里关联不同Fragment,并通过fragment取到view和context。
public class LoginPresenter<C extends IController> extends BaseCashBarPresenter<C>
  • Fragment定义Presenter泛型,这样可以方便取到Presenter。
public abstract class AbstractSupportFragment<P extends AbstractControllerVisitorAsPresenter> extends Fragment implements IFragment<P> {
  private P mPresenter;
  @Override
  public P bindPresenter() {
    return TUtil.getT(this, 0, getClass());
  }
}

public class TUtil {
    public static <T> T getT(Object o, int i, Class<?>... parameterTypes) {
        try {
            Type type = o.getClass().getGenericSuperclass();
            if(type instanceof ParameterizedType){
                return ((Class<T>) ((ParameterizedType) type).getActualTypeArguments()[i])
                        .getDeclaredConstructor(parameterTypes)
                        .newInstance(o);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassCastException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

2.Java的Generics与C++的Template
由于Java的Generics设计在C++的Template之后,因此Java的Generics设计吸取Template的很多经验和教训。

  • Generics的声明是需要进行类型检查的,而Template不提供这一功能,这使得Generics的使用更加安全。
  • Java的Generics程序只需要编译一次,以后所有程序就可以复用这个类字节码,而Template的实现是为每一个使用Template变量编译成一个新类,这会引起一定的冗余代码。

3.Generics的定义:
3.1类和接口定义
以下是最简单的Generics类定义,定义了一个参数化类型T1:

interface MyList<T1> {….. }

以下是支持多个参数化类型的接口:

interface MyList<T1,T2,T3> {….. }

Java支持带有限制的参数化类型。在下面的例子中,T1的类型必须实现类Comparable接口,T2类型必须为Component类的子类。

interface MyList<T1 implements Comparable, T2 extends Component> {}


复杂的定义可以带有限定的声明,甚至可以使用向前引用。

class Test<A implements ConvertibleTo<B>, B implements ConvertibleTo<A>{}

对于class的定义,基本与interface相同,此处不再详述。

3.2 方法的定义
在方法中,通过定义参数化类型,可以提高方法的抽象级别,提高其可复用性。方法的参数化类型列表放在方法修饰符的后面,返回值的前面。如:

public static  <Elem> void swap(Elem[] a,int i,int j)
   Elem temp=a[i];
   a[i]=a[j]
   a[j]=temp;
}

3.3 实际例子
一但Java语言支持了Generics,Collection中的大部分类将用Generics方式重写。例:使用Generics重写的Hashtable的定义

public class Hashtable<K,V> 
extends Dictionary<K,V> 
implements Map<K,V>, java.lang.Cloneable, java.io.Serializable {  
public V put(K key, V value) {……}
…….
}

与以前的Hashtable定义不同的是,它增加了两个用于表达Key和Value参数化类型(K,V),由于Dictionary和Map都将支持Generics,因此它们都使用Generics的表达方式。

4.Generics的概念题
1. 同一个Generics类所定义的不同参数化类型的对象,它们的Class类型相同

Vector<String> v1=new Vector<String>();
Vector<Integer> v2=new Vector<Integer>();
Assert(V1.getClass().equals(v2.getClass())); //返回true

2. 同一个Generics类所定义的不同参数化类型的对象之间是不能进行转化的;Object不能够转化成Generics类型,但是Generics类可以转化成Object。

void  method(Vector<String> v) {};
…
Vector<Integer> v2=new Vector<Integer>();
method(v2);//编译失败
Class Dictionary<A,B> extends Object{}
Class Hashtable<A,B> extends Dictionary<A,B> {}
Dictionary<String,Integer> d=new Dictionary<String,Integer>();
Hashtable<String,Integer> h=new Hashtable <String,Integer>();
Hashtable<Float,Double> hfd=new Hashtable<Float,Double>();
Object o=new Object();
1) d= (Dictionary<String,Integer>)h//编译成功,运行成功;它们具有父子类关系。
2) h=(Hashtable<String,Integer>)d;// 编译成功,运行失败;它们具有父子类关系。
3) h=(Hashtable<String,Integer>)o;//编译失败,Object不能转化成Generics类;
4) hfd=(Hashtable<Float,Double>)d;//编译失败;

 

posted on 2015-06-30 11:14  joannae  阅读(675)  评论(0编辑  收藏  举报

导航