200809281411java(十)--generics

java(十)--generics: 2008年09月28日 星期日 

Note: generics is supported from J2SE5.0, so the jsdk1.4.2 compiler will report error while compiling.

Now if using JDK6 (i.e. j2sdk 1.6.0), there will be warning on unchecked (or non-generic type), use javac -Xlint option will show the places with unchecked type. e.g.

public class WarningDemo {

public static void main(String[] args){

Box<Integer> bi;

bi = createBox();

}

/**

* Pretend that this method is part of an old library,

* written before generics. It returns

* Box instead of Box<T>.

*/

static Box createBox(){

return new Box();

}

}

Generic type declaration: "public class Box<T>", You can think of a generic type invocation as being similar to an ordinary method invocation, but instead of passing an argument to a method, you're passing a type argumentInteger in this case — to the Box class itself,An invocation of a generic type is generally known as a parameterized type

It's important to understand that type variables are not actually types themselves. In the above examples, you won't find T.java or T.class anywhere on the filesystem. Furthermore, T is not a part of the Box class name. In fact during compilation, all generic information will be removed entirely, leaving only Box.class on the filesystem.

e.g.:

/**
 * Generic version of the Box class. 
 */
public class Box<T> {

    private T t; // T stands for "Type"          

    public void add(T t) {
        this.t = t;
    }

    public T get() {
        return t;
    }
}
  
public class BoxDemo3 {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
		// The following line will give compile error since "wrong type"
		  integerBox.add("10");
      // The following line is correct
        integerBox.add(new Integer(10));
        Integer someInteger = integerBox.get(); // no cast!
        System.out.println(someInteger);
    }
}

由上例BoxDemo3中的integerBox.add("10");的"10"是String type而非Integer type,由於Box class中用了generic type,因此 ,compile時會error,否則run time才有error。T 稱為type parameter。

type parameter naming convention:By convention, type parameter names are single, uppercase letters. The most commonly used type parameter names are:

  • E - Element (used extensively by the Java Collections Framework)
  • K - Key
  • N - Number
  • T - Type
  • V - Value
  • S,U,V etc. - 2nd, 3rd, 4th type

Generic Methods and Constructors: Type parameters can also be declared within method and constructor signatures to create generic methods and generic constructors. This is similar to declaring a generic type, but the type parameter's scope is limited to the method or constructor in which it's declared.

type inference:e.g.

public static <U> void fillBoxes(U u, List<Box<U>> boxes) {

for (Box<U> box : boxes) { box.add(u); }

}

bounded type parameters : public <U extends Number> void inspect(U u), e.g.(the boldfaced line below)

public class BoxBT<T> {

private T t;

public void add(T t) {

this.t = t;

}

public T get() {

return t;

}

public <U extends Number> void inspect(U u){

System.out.println("T: " + t.getClass().getName());

System.out.println("U: " + u.getClass().getName());

}

public static void main(String[] args) {

BoxBT<Integer> integerBox = new BoxBT<Integer>();

integerBox.add(new Integer(10));

integerBox.inspect("some text"); // error: this is still String!

}

}

subtyping:As you already know, it's possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes.In object-oriented terminology, this is called an "is a" relationship. Since an Integer is a kind of Object, the assignment is allowed.The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument, and any subsequent invocation of add will be allowed if the argument is compatible with Number:

    Box<Number> box = new Box<Number>();
    box.add(new Integer(10)); // OK
    box.add(new Double(10.1)); // OK


But,look at the following example:

public void boxTest(Box<Number> n)

{ // method body omitted

}

What type of argument does it accept? By looking at its signature, we can see that it accepts a single argument whose type is Box<Number>. But what exactly does that mean? Are you allowed to pass in Box<Integer> or Box<Double>, as you might expect? Surprisingly, the answer is "no", because Box<Integer> and Box<Double> are not subtypes of Box<Number>.

collection interface:

// A cage is a collection of things, with bars to keep them in. interface Cage<E> extends Collection<E>;

A lion is a kind of animal, so Lion would be a subtype of Animal:

	interface Lion extends Animal {}
	Lion king = ...;

Where we need some animal, we're free to provide a lion:

	Animal a = king;

A lion can of course be put into a lion cage:

	Cage<Lion> lionCage = ...;
	lionCage.add(king);

and a butterfly into a butterfly cage:

	interface Butterfly extends Animal {}
	Butterfly monarch = ...;
	Cage<Butterfly> butterflyCage = ...;
	butterflyCage.add(monarch);

But what about an "animal cage"? English is ambiguous, so to be precise let's assume we're talking about an "all-animal cage":

 

	Cage<Animal> animalCage = ...;

This is a cage designed to hold all kinds of animals, mixed together. It must have bars strong enough to hold in the lions, and spaced closely enough to hold in the butterflies. Such a cage might not even be feasible to build, but if it is, then:

	animalCage.add(king);
	animalCage.add(monarch);

Since a lion is a kind of animal (Lion is a subtype of Animal), the question then becomes, "Is a lion cage a kind of animal cage? Is Cage<Lion> a subtype of Cage<Animal>?". By the above definition of animal cage, the answer must be "no". This is surprising! But it makes perfect sense when you think about it: A lion cage cannot be assumed to keep in butterflies, and a butterfly cage cannot be assumed to hold in lions. Therefore, neither cage can be considered an "all-animal" cage:

	animalCage = lionCage;	// compile-time error
        animalCage = butterflyCage; // compile-time error

Without generics, the animals could be placed into the wrong kinds of cages, where it would be possible for them to escape.

wildcard:a cage designed not for any kind of animal, but rather for some kind of animal whose type is unknown. In generics, an unknown type is represented by the wildcard character "?". To specify a cage capable of holding some kind of animal:

Cage<? extends Animal> someCage = ...;

Read "? extends Animal" as "an unknown type that is a subtype of Animal, possibly Animal itself", which boils down to "some kind of animal". This is an example of a bounded wildcard, where Animal forms the upper bound of the expected type. If you're asked for a cage that simply holds some kind of animal, you're free to provide a lion cage or a butterfly cage.

Note: It's also possible to specify a lower bound by using the super keyword instead of extends>. The code <? super Animal>>, therefore, would be read as "an unknown type that is a supertype of Animal, possibly Animal itself". You can also specify an unknown type with an unbounded wilcard, which simply looks like <?>>. An unbounded wildcard is essentially the same as saying <? extends Object>.

While Cage<Lion> and Cage<Butterfly> are not subtypes of Cage<Animal>, they are in fact subtypes of Cage<? extends Animal>:

someCage = lionCage; // OK

someCage = butterflyCage; // OK

So now the question becomes, "Can you add butterflies and lions directly to someCage?". As you can probably guess, the answer to this question is "no".

	someCage.add(king);	// compiler-time error
	someCage.add(monarch);	// compiler-time error
        

bounded type:There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter.

public <U extends Number> void inspect(U u){ ...

}

To specify additional interfaces that must be implemented, use the & character, as in:

<U extends Number & MyInterface>

type erasure: When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. so,The operations shown in bold are meaningless at runtime because the compiler removes all information about the actual type argument (represented by the type parameter E) at compile time. as shown below,

public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();   //Compiler error
        E[] iArray = new E[10]; //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
}

java相關文章:

javajava(一)-第一個java程式java(二)--oop概念java(三)--java基本語法java(四)--classes & objectsjava(五)--interfacesjava(六)--interfaces的實例java(七)-- inheritancesjava(八)--java basic classes in APIjava(九)--Thinking in Java;java(十)--generics;

為何學java,見eclipse

回應
嬰兒潮退休指南





Powered by Xuite
    沒有新回應!
關鍵字
部落格的朋友們