Thinking in java 4th &the pitfall of Generic

Odinary classes and methods work with specific type :either primitives or class types .if you are writing code that might be used across more type .the rigidity can be over constraining.

Generalization that means 泛化 in Chinese .

the polymorphism mechanism can implement the generalization by passing the class which derived by the base class into the method ,so in this block of the method it can implement the generalization without knowing the exact type but should derived by the base class otherwise it will give you an exception .but it also lack of flexibility .Although you can put the interface to it parameter without knowing the class which is not created yet .but it is also too restrictive .An interface still requires that your code work with the specific interface .You should write even more general code if you could say that your code works with some unspecific type ,rather than a specific interface or class .So it will come up the concept of generics this is the one of more significant changes in Java SE5 .Generics implement the concept of parameterized types ,which allow multiple types .

//: generics/Holder3.java
public class Holder3<T> {
private T a;
public Holder3(T a) { this.a = a; }
public void set(T a) { this.a = a; }
public T get() { return a; }
public static void main(String[] args) {
Holder3<Automobile> h3 =
new Holder3<Automobile>(new Automobile());
Automobile a = h3.get(); // No cast needed
// h3.set("Not an Automobile"); // Error
// h3.set(1); // Error
}
} ///:~

Consider this situation where we put the generic T subsitute the the object (any class should be derived by Object). 

The core of the java generics :you tell it what type you want to use ,and it takes care of the details.

The generics can be also used is the class ,which specific the general name between the angle bracket.if you want to use the tuple of the generics ,you can also specific the different general names between the angle bracket but should be seperated by the comma.

//: generics/Fibonacci.java
// Generate a Fibonacci sequence.
import net.mindview.util.*;
public class Fibonacci implements Generator<Integer> {
private int count = 0;
public Integer next() { return fib(count++); }
private int fib(int n) {
if(n < 2) return 1;
return fib(n-2) + fib(n-1);
}
public static void main(String[] args) {
Fibonacci gen = new Fibonacci();
for(int i = 0; i < 18; i++)
System.out.print(gen.next() + " ");
}
} /* Output:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*///:~

Consider in this situation that you cannot use the primitives as the type parameters ,however javaSE5 conveniently added autoboxing to convert from primitives types to wrapper types and back , use the generics in the interface is no difference than using the generics in the class .but when you wanna use the generics in the method it will be somewhat different .

To define a generics method ,you simply place a generic parameter list before the return value .Notice that with a generic class ,you must specify the type parameters when you instantiate the class .But with the generic method ,you do not usually have to specify the parameter types .Because the compiler will figure that out for you .This is called the type argument inference .

Because of this feature we can do something like this ,also we can simplify the create the container without use the parameter type tuple or not .

//: net/mindview/util/New.java
// Utilities to simplify generic container creation
// by using type argument inference.
package net.mindview.util;
import java.util.*;
public class New {
public static <K,V> Map<K,V> map() {
return new HashMap<K,V>();
}
public static <T> List<T> list() {
return new ArrayList<T>();
}
public static <T> LinkedList<T> lList() {
return new LinkedList<T>();
}
public static <T> Set<T> set() {
return new HashSet<T>();
}
public static <T> Queue<T> queue() {
return new LinkedList<T>();
}
// Examples:
public static void main(String[] args) {
Map<String, List<String>> sls = New.map();
List<String> ls = New.list();
LinkedList<String> lls = New.lList();
Set<String> ss = New.set();
Queue<String> qs = New.queue();
}
} ///:~

//: generics/SimplerPets.java
import typeinfo.pets.*;
import java.util.*;
import net.mindview.util.*;
public class SimplerPets {
public static void main(String[] args) {
Map<Person, List<? extends Pet>> petPeople = New.map();
// Rest of the code is the same...
}
} ///:~

But the type inference does not work for anything other than assignment .

The generic methods and variables argument lists coexist nicely.

//: generics/GenericVarargs.java
import java.util.*;
public class GenericVarargs {
public static <T> List<T> makeList(T... args) {
List<T> result = new ArrayList<T>();
for(T item : args)
result.add(item);
return result;
}
public static void main(String[] args) {
List<String> ls = makeList("A");
System.out.println(ls);
ls = makeList("A", "B", "C");
System.out.println(ls);
ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));
System.out.println(ls);
}
} /* Output:
[A]
[A, B, C]
[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W,
X, Y, Z]
*///:~

the erasure : that means if you put the T in the class ,that the T would look like it upper cast type ,such you put the class<T> as the member of the class ,it will erase to as the class ,which means no T ,just the class itself ,And another case for erasure is ,if you put the T data as the member of the class .it will be erased as the Object, so inside the class ,without restricting the boundaries of the type ,the type will be original object as we've seen before.But why the java designer want to add the erasure to this part? the migration compatibility would be the reason for this confusion .So before the Java SE5 was published .there are not generics in this language .So java designer want to compatible for the previous version of Java and the classes or methods which not applied the generics programming .That is .So they are create the new features of erasure into this regoin.

and I used the purely template programming in c++ .every template type parameter would be inference in the complie process

so I even not need to assume that the method  in which the parameters I put  whether it is the correct type to call the specific method or allocate the new memory of the template type.because if I do something wrong with this code .the IDE will tell me where I do the bad job and show the error message below the meesage tab .But when I first learning Java with the this book which is original edition of English language .It really confuses me why the Java do the so so ........messy jobs and it lack of the logic . So at  that time I really not want  to touch the generics in java except restricting the boundaries of generic type parameters. 

 

The generics doesn't have built-in covariance the compiler and runtime system cannot know what you want to do with your type and the rules should be .Sometime you'd like to establish some kind of upcasting relationship between the two,this is what the wildcards allow .

//: generics/GenericsAndCovariance.java
import java.util.*;
public class GenericsAndCovariance {
public static void main(String[] args) {
// Wildcards allow covariance:
List<? extends Fruit> flist = new ArrayList<Apple>();
// Compile Error: can’t add any type of object:
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // Legal but uninteresting
// We know that it returns at least Fruit:
Fruit f = flist.get(0);
}
} ///:~

in the sample above you can not put anything in the list even the object because the array doesnt know which type it will hold in its element .

The attempt to create the new T () in java wont work,partly because of erasure.and partly because the compiler cannot verify that the T has the default constructor .The solution in java is to pass in a factory object ,and use that to make new instance 

You can’t assign a generic involving Apples to a generic involving Fruit.

The type of flist is now List<? extends Fruit>, which you can read as "a list of any type that’s inherited from Fruit." This doesn’t actually mean that the List will hold any type of Fruit,however. The wildcard refers to a definite type

If the only constraint is that the List hold a specific Fruit or subtype of Fruit, but you don’t actually care what it is, then what can you do with such a List? If you don’t know what type the List is holding, how can you safely add an object?

because now you can’t even add an Apple to a List that you just said would hold Apples. Yes, but the compiler doesn’t know that. A List<? extends Fruit> could legally point to a List<Orange>. Once you do this kind of "upcast," you lose the ability to pass anything in, even an Object.

because the set( ) argument is also "? Extends Fruit," which means it can be anything and the compiler can’t verify type safety for "anything."

Contravariance

It’s also possible to go the other way, and use supertype wildcards.  you say that the wildcard is bounded by any base class of a particular class, by specifying <? super MyClass> or even using a type parameter: <? super T>

//: generics/SuperTypeWildcards.java
import java.util.*;
public class SuperTypeWildcards {
static void writeTo(List<? super Apple> apples) {
apples.add(new Apple());
apples.add(new Jonathan());
// apples.add(new Fruit()); // Error
}
} ///:~

The argument apples is a List of some type that is the base type of Apple.thus you know that it is safe to add an Apple or a subtype of Apple.

Supertype bounds relax the constraints on what you can pass into a method

import java.util.*;
public class GenericWriting {
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static List<Apple> apples = new ArrayList<Apple>();
static List<Fruit> fruit = new ArrayList<Fruit>();
static void f1() {
writeExact(apples, new Apple());
// writeExact(fruit, new Apple()); // Error:
// Incompatible types: found Fruit, required Apple
}
static <T> void
writeWithWildcard(List<? super T> list, T item) {
list.add(item);
}
static void f2() {
writeWithWildcard(apples, new Apple());
writeWithWildcard(fruit, new Apple());
}
public static void main(String[] args) { f1(); f2(); }

In writeWithWildcard( ), the argument is now a List<? super T>, so the List holds a specific type that is derived from T; thus it is safe to pass a T or anything derived from T as an argument to List methods. You can see this in f2( ), where it’s still possible to put an Apple into a List<Apple>, as before, but it is now also possible to put an Apple into a List<Fruit>, as you expect.

//: generics/Wildcards.java
// Exploring the meaning of wildcards.
public class Wildcards {
// Raw argument:
static void rawArgs(Holder holder, Object arg) {
// holder.set(arg); // Warning:
// Unchecked call to set(T) as a
// member of the raw type Holder
// holder.set(new Wildcards()); // Same warning

// Can’t do this; don’t have any ‘T’:
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
// Similar to rawArgs(), but errors instead of warnings:
static void unboundedArg(Holder<?> holder, Object arg) {
// holder.set(arg); // Error:
// set(capture of ?) in Holder<capture of ?>
// cannot be applied to (Object)
// holder.set(new Wildcards()); // Same error
// Can’t do this; don’t have any ‘T’:
// T t = holder.get();
// OK, but type information has been lost:
Object obj = holder.get();
}
static <T> T exact1(Holder<T> holder) {
T t = holder.get();
return t;
}
static <T> T exact2(Holder<T> holder, T arg) {
holder.set(arg);
T t = holder.get();
return t;
}
static <T>
T wildSubtype(Holder<? extends T> holder, T arg) {
// holder.set(arg); // Error:
// set(capture of ? extends T) in
// Holder<capture of ? extends T>
// cannot be applied to (T)
T t = holder.get();
return t;
}
static <T>
void wildSupertype(Holder<? super T> holder, T arg) {
holder.set(arg);
// T t = holder.get(); // Error:
// Incompatible types: found Object, required T
// OK, but type information has been lost:
Object obj = holder.get();
}

In wildSubtype( ), the constraints on the type of Holder are relaxed to include a Holder of anything that extends T. Again, this means that T could be Fruit, while holder could legitimately be a Holder<Apple>. To prevent putting an Orange in a Holder<Apple>, the call to set( ) (or any method that takes an argument of the type parameter) is disallowed. However, you still know that anything that comes out of a Holder<? extends Fruit> will at least be Fruit, so get( ) (or any method that produces a return value of the type parameter) is allowed. Supertype wildcards are shown in wildSupertype( ), which shows the opposite behavior of wildSubtype( ): holder can be a container that holds any type that’s a base class of T. Thus, set( ) can accept a T, since anything that works with a base type will polymorphically work with a derived type (thus a T). However, trying to call get( ) is not helpful, because the type held by holder can be any supertype at all, so the only safe one is Object.

 

Capture conversion only works in situations where, within the method, you need to work with the exact type. 

public class CaptureConversion {
static <T> void f1(Holder<T> holder) {
T t = holder.get();
System.out.println(t.getClass().getSimpleName());
}
static void f2(Holder<?> holder) {
f1(holder); // Call with captured type
}
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Holder raw = new Holder<Integer>(1);
// f1(raw); // Produces warnings
f2(raw); // No warnings
Holder rawBasic = new Holder();
rawBasic.set(new Object()); // Warning
f2(rawBasic); // No warnings
// Upcast to Holder<?>, still figures it out:
Holder<?> wildcarded = new Holder<Double>(1.0);
f2(wildcarded);
}
} /* Output:
Integer
Object
Double
*///:~

So with the unbounded type f2 in which the f1 is called but the f1 need the exact type ,but in this situation ,it will work fine.But you can not determine the actual type which it is put in the parameter of method .

//: generics/MultipleInterfaceVariants.java
// {CompileTimeError} (Won’t compile)
interface Payable<T> {}
class Employee implements Payable<Employee> {}
class Hourly extends Employee
implements Payable<Hourly> {} ///:~

A class cannot implement two variants of the same generic interface. Because of erasure,Hourly won’t compile because erasure reduces Payable<Employee> and Payable<Hourly> to the same class, Payable, and the above code would mean that you’d be implementing the same interface twice. 

//: generics/SelfBounding.java
class SelfBounded<T extends SelfBounded<T>> {
T element;
SelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() { return element; }
}
class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {} // Also OK
class C extends SelfBounded<C> {
C setAndGet(C arg) { set(arg); return get(); }
} 
class D {}
// Can’t do this:
// class E extends SelfBounded<D> {}
// Compile error: Type parameter D is not within its bound
// Alas, you can do this, so you can’t force the idiom:
class F extends SelfBounded {}
public class SelfBounding {
public static void main(String[] args) {
A a = new A();
a.set(new A());
a = a.set(new A()).get();
a = a.get();
C c = new C();
c = c.setAndGet(new C());
}
} ///:~

the self-bound class which means it only can use the type of the definition itself.which restrict the bound .the self-bounding constraint serves only to force the inheritance relationship.

It’s also possible to use self-bounding for generic methods:

//: generics/SelfBoundingMethods.java
public class SelfBoundingMethods {
static <T extends SelfBounded<T>> T f(T arg) {
return arg.set(arg).get();
}
public static void main(String[] args) {
A a = f(new A());
}
} ///:~

This prevents the method from being applied to anything but a self-bounded argument of the form shown.

Summary:

the subtype cannot be set but can be gotten.And the supertype cannot be gotten but can be set .

autoboxing doesn’t apply to arrays

the various of problem in generic from java programmming

any basic type can not be the paramter of the type.

as I mentions above in this chapter early .you will discover the one of the restriction is that you can not take the basic type as the parameter in the generic. 

what solve this problem is using the packaging class of basic type and the automatic package mechanism from java SE5.if create a ArrayList<Integer>,and make the basic type int refer to this container .so that you will find out that integer will be conversed to int each other bidirectionally by the automatic package mechanism .

BUT THE AUTOMATIC PACKAGE MECHANISM CANNOT USED IN THE ARRAY ,IT WILL CONFUSE YOU IN THE UNEXPECTED BEHAVIOR 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值