2,简单泛型
一个元组类库
return只能返回一个对象,要是想返回多个对象,解决方法就是让返回的这个对象持有多个想返回的对象,但是每次返回都需要不同类型的对象,那就需要创建很多类,使用泛型可以很好地避免此类问题。同时,我们在编译时就可以确保类型安全。
这种概念就成为元组。这个容器对象允许读取对象,但是允许向其中添加对象。
package com.generic;
public class TwoTuple<A,B> {
public final A first;
public final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
public String toString(){
return "("+first+ " "+second+")";
}
}
这里变量设置为Final,可以保证其不被修改。
可以使用继承机制实现长度更长的元组:
package com.generic;
public class ThreeTuple<A,B,C> extends TwoTuple<A, B> {
public final C third;
public ThreeTuple(A a,B b,C c){
super(a,b);
third = c;
}
public String toString(){
return "("+first+ " "+second+" "+third+")";
}
}
实现更长的元祖以此类推。
使用元组:
package com.generic;
class Amp{}
public class TupleTest {
static TwoTuple<String,Integer> f(){
return new TwoTuple<String,Integer>("hi",47);
}
static ThreeTuple<Amp,String,Integer> g(){
return new ThreeTuple(new Amp(),"three",48);
}
public static void main(String[] args) {
System.out.println(f());
System.out.println(g());
}
}
一个堆栈类
不用Linkedlist的Stack:
package com.generic;
public class LinkedStack<T> {
private static class Node<U>{
U item;
Node<U> next;
Node(){
item = null;
next = null;
}
Node(U item, Node<U> next){
this.item = item;
this.next = next;
}
boolean end(){
return item == null&&next == null;
}
}
private Node<T> top = new Node<T>();
public void push(T item){
top = new Node<T>(item,top);
}
public T pop(){
T result = top.item;
if(!top.end()){
top = top.next;
}
return result;
}
public static void main(String[] args) {
LinkedStack<String> lss = new LinkedStack<>();
for(String s:"Phasers on stun!".split(" ")){
lss.push(s);
}
String s;
while((s = lss.pop()) != null){
System.out.println(s);
}
}
}
/*
输出:
stun!
on
Phasers
*/
很有意思的例子。
个人理解相当于一个链表的样子。
RandomList
一个使用泛型实现随机访问的例子:
package com.generic;
import java.util.ArrayList;
import java.util.Random;
public class RandomList<T> {
private ArrayList<T> storage = new ArrayList<T>();
private Random rand = new Random(47);
public void add(T item){
storage.add(item);
}
public T select(){
return storage.get(rand.nextInt(storage.size()));
}
public static void main(String[] args) {
RandomList<String> rs = new RandomList<>();
for(String s:"the quick brown fox jumped over".split(" ")){
rs.add(s);
}
for(int i = 0; i < 5; i++){
System.out.print(rs.select()+" ");
}
}
}
/*
输出:
brown over quick over quick
*/
3,泛型接口
package com.generic;
public interface generator<T> {
T next() throws InstantiationException, IllegalAccessException;
}
定义一些Coffee类:
package com.generic;
public class Coffee {
private static long counter = 9;
private final long id = counter++;
public String toString(){
return getClass().getSimpleName();
}
}
public class Latte extends Coffee{}
....
编写类,实现这个接口,随机生成不同类型的Coffee对象:
package com.generic;
import java.util.*;
public class CoffeeGenerator implements generator<Coffee>,Iterable<Coffee>{
private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,Americano.class,Breve.class};
private static Random rand = new Random(47);
public CoffeeGenerator(){}
private int size = 0;
public CoffeeGenerator(int sz){
size = sz;
}
@Override
public Coffee next() throws InstantiationException, IllegalAccessException {
return (Coffee)types[rand.nextInt(types.length)].newInstance();
}
class CoffeeIterator implements Iterator<Coffee>{
int count = size;
@Override
public boolean hasNext() {
return count > 0;
}
@Override
public Coffee next() {
count--;
Coffee coffee = null;
try {
coffee = CoffeeGenerator.this.next();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return coffee;
}
}
@Override
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException{
CoffeeGenerator gen = new CoffeeGenerator();
for(int i = 0; i < 5; i++){
System.out.print(gen.next()+" ");
}
System.out.println();
for(Coffee c: new CoffeeGenerator(5)){
System.out.print(c+" ");
}
}
}
/*
输出:
Americano Latte Americano Mocha Mocha
Breve Americano Latte Cappuccino Cappuccino
*/
接口泛型的另一个实例:
package com.generic;
import java.util.Iterator;
public class Fibonacci implements generator<Integer>,Iterable<Integer>{
private int count = 0;
private int n = 0;
public Fibonacci(){}
public Fibonacci(int n){
this.n = n;
}
@Override
public Integer next() throws InstantiationException, IllegalAccessException {
return fib(count++);
}
private int fib(int n){
if(n < 2){
return 1;
}
return fib(n-2)+fib(n-1);
}
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>(){
@Override
public boolean hasNext() {
return n > 0;
}
@Override
public Integer next() {
n--;
int i = 0;
try {
i = Fibonacci.this.next();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return i;
}
};
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException{
Fibonacci gen = new Fibonacci();
for(int i = 0;i < 18;i++){
System.out.print(gen.next()+" ");
}
System.out.println();
for(int i: new Fibonacci(18)){
System.out.print(i+" ");
}
}
}
/*
输出:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
*/
4,泛型方法
package com.generic;
public class GenericMethods {
public static <T> void f(T x){
System.out.println(x.getClass().getName());
}
public static void main(String[] args) {
GenericMethods gm = new GenericMethods();
gm.f("");
gm.f(1.0);
gm.f(1.0f);
gm.f('c');
gm.f(gm);
f("");
}
}
泛型方法可以不依赖与泛型类,如果使用泛型方法可以取代泛型类,那就尽量使用泛型方法!
这种利用参数判断泛型类型。下面使用返回值判断泛型类型。
类型参数推断
package com.generic;
import java.util.*;
public class New {
public static <K,V> Map<K,V> map(){
return new HashMap<K,V>();
}
public static <T> void list(){
}
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>();
}
public static void main(String[] args) {
Map<String,List<String>> sls = New.map();
New.list();
LinkedList<String> lls = New.lList();
Set<String> ss = New.set();
Queue<String> qs = New.queue();
}
}
但是这种方法只在赋值操作时有效,但在其他地方并不适用,看如下示例:
package com.generic;
import java.util.*;
public class LimitsOfInference {
static void f(Map<Coffee,Coffee> coffee){
System.out.println(coffee.getClass());
coffee.put(new Coffee(), new Latte());
}
public static void main(String[] args) {
f(New.map());
}
}
这段代码书上说不可以,但是实际是可以的!
可以显示的指明类型:
package com.generic;
import java.util.Map;
public class ExplicitTypeSpecification {
static void f(Map<Coffee,Coffee> coffee){
System.out.println(coffee.getClass());
coffee.put(new Coffee(), new Latte());
}
public static void main(String[] args) {
f(New.<Coffee,Coffee>map());
}
}
这种必须在方法前指明调用者,在方法前指明泛型类型,这种方法不常用。
可变参数与泛型方法
package com.generic;
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);
}
}
用于Generator的泛型方法
就是一个使用泛型的例子:
package com.generic;
import java.util.*;
public class Generators {
public static <T> Collection<T>
fill(Collection<T> coll,generator<T> gen,int n) throws InstantiationException, IllegalAccessException{
for(int i = 0; i < n;i++){
coll.add(gen.next());
}
return coll;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Collection<Coffee> coffee = fill(new ArrayList<Coffee>(),new CoffeeGenerator(),4);
for(Coffee c:coffee){
System.out.println(c);
}
}
}
一个通用的Generator
package com.generic;
public class BasicGeneratorDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
for(int i = 0; i < 5; i++){
System.out.println(gen.next());
}
}
}
package com.generic;
public class CountedObject {
private static long counter = 0;
private final long id = counter++;
public long id(){
return id;
}
public String toString(){
return "CountedObject"+id;
}
}
package com.generic;
public class BasicGeneratorDemo {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
for(int i = 0; i < 5; i++){
System.out.println(gen.next());
}
}
}
挺简单的,不解释了。
简化元组的使用
package com.generic;
public class Tuple {
public static <A,B> TwoTuple<A,B> tuple(A a, B b){
return new TwoTuple<A,B>(a,b);
}
public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b,C c){
return new ThreeTuple<A,B,C>(a,b,c);
}
}
package com.generic;
import static com.generic.Tuple.*;
public class TupleTest2 {
static TwoTuple<String,Integer> f(){
return tuple("hi",47);
}
static TwoTuple f2(){
return tuple("hi",47);
}
static ThreeTuple<Amphibian,String,Integer> h(){
return tuple(new Amphibian(),"hi",47);
}
public static void main(String[] args) {
System.out.println(f());
System.out.println(f2());
System.out.println(h());
}
}
一个Set实用工具
有一个例子,略!
5,匿名内部类
泛型的一个好处就是可以简单而安全的创建复杂的模型,下面实例很容易的创建了List元组:
package com.generic;
import java.util.*;
public class TupleList<A,B,C> extends ArrayList<ThreeTuple<A,B,C>> {
public static void main(String[] args){
TupleList<Amphibian,String,Integer> t1 = new TupleList<>();
t1.add(TupleTest.g());
t1.add(TupleTest.g());
for(ThreeTuple<Amphibian,String,Integer> i : t1){
System.out.println(i);
}
}
}
另一个复杂模型,真够复杂的,有点搞不懂:
package com.generic;
import java.util.*;
class Product{
private final int id;
private String description;
private double price;
public Product(int IDumber,String descr,double price){
id = IDumber;
description = descr;
this.price = price;
System.out.println(toString());
}
public String toString(){
return id+":"+description+".price:$"+price;
}
public void priceChange(double change){
price += change;
}
public static generator<Product> generator = new generator<Product>(){
private Random rand = new Random(47);
public Product next(){
return new Product(rand.nextInt(1000),"Test",
Math.round(rand.nextDouble()*1000.0)+0.99);
}
};
}
class Shelf extends ArrayList<Product>{
public Shelf(int nProducts){
try {
Generators.fill(this, Product.generator, nProducts);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Aisle extends ArrayList<Shelf>{
public Aisle(int nShelves,int nProducts){
for(int i = 0; i < nShelves;i++){
add(new Shelf(nProducts));
}
}
}
class CheckoutStand{}
class Office{}
public class Store extends ArrayList<Aisle>{
private ArrayList<CheckoutStand> checkouts = new ArrayList<CheckoutStand>();
private Office office = new Office();
public Store(int nAisles, int nShelves,int nProducts){
for(int i = 0; i < nAisles;i++){
add(new Aisle(nShelves,nProducts));
}
}
public String toString(){
StringBuilder result = new StringBuilder();
for(Aisle a : this){
for(Shelf s: a){
for(Product p : s){
result.append(p);
result.append("\n");
}
}
}
return result.toString();
}
public static void main(String[] args){
System.out.println(new Store(14,5,10));
}
}
逻辑有点复杂,智商不够用。
7,擦除的神秘之处
尽管可以声明ArrayList.class
但是不能声明ArrayList<Integer>
package com.generic;
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);
}
}
/*
输出:
true
*/
利用反射得到泛型信息:
package com.generic;
import java.util.*;
class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Particle<POSITION,MOMENTUM>{}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<>();
Map<Frob,Fnorkle> map = new HashMap<>();
Quark<Fnorkle> quark = new Quark<>();
Particle<Long,Double> p = new Particle<>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
}
}
/*
输出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*/
输出的只是用作参数的占位符的表示符。
因此,残酷的现实是:
在泛型代码内部,无法获得任何有关泛型参数类型的信息
java泛型是使用擦除来实现的,这意味着使用泛型时,任何具体的类型信息都被擦除了!
C++的方式
看不懂,不看
迁移兼容性
好多字,大体的意思就是泛型不是在java出现时就有的特性,而是后来添加的。为了不使之前的类库代码报废,只好使用了向前兼容的方式,所有使用了擦除的方式。
擦除的问题
擦除的代价是显著的,泛型不能用于显示的引用运行时类型的操作之中,例如转型,instanceof,和new表达式。因为所有关于参数的类型的信息都丢失了,所以无论何时,在编写泛型代码时,必须时刻提醒自己,这只是看起来拥有有关参数的类型信息而已,因此,如果看到如下代码:
class Foo<T>{
T var;
}
创建实例时:
Foo<Cat> f = new Foo<Cat>();
Foo类的代码应该知道工作在Cat类之上,而泛型语法也强烈暗示,在整个类的所有地方,类型T都被Cat替换。但是事实并非如此,当在编写这个类的代码时,必须提醒自己:“这只是一个Object”;
所以,擦除和迁移兼容性以为这使用泛型并不是强制的,不使用泛型只会出现警告而不是报错!
package com.generic;
class GenericBase<T>{
private T element;
public void set(T arg){
element = arg;
System.out.println("set");
}
public T get(){
return element;
}
}
class Derived1<T> extends GenericBase<T>{}
class Derived2 extends GenericBase{}//不警告
public class ErasureAndInheritance {
public static void main(String[] args) {
Derived2 d2 = new Derived2();
Object obj = d2.get();
d2.set(obj);//警告
}
}
边界处的动作
正式有了擦除,泛型可以表示没有任何意义的实物:
package com.generic;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ArrayMaker<T> {
private Class<T> kind;
public ArrayMaker(Class<T> kind){
this.kind = kind;
}
T[] create(int size){
//T[] t = new T[]{kind.newInstance(),kind.newInstance()};报错
return (T[])Array.newInstance(kind, size);
}
public static void main(String[] args) {
ArrayMaker<String> stringMaker = new ArrayMaker<>(String.class);
String[] stringArray = stringMaker.create(9);
System.out.println(Arrays.toString(stringArray));
}
}
/*
输出:
[null, null, null, null, null, null, null, null, null]
*/
注意,在泛型中,推荐使用Array.newInstance()。
创建一个容器而不是一个数组:
package com.generic;
import java.util.*;
public class FilledListMaker<T> {
List<T> create(T t, int n){
List<T> result = new ArrayList<T>();
for(int i = 0; i <n;i++){
result.add(t);
}
return result;
}
public static void main(String[] args) {
FilledListMaker<String> stringMaker = new FilledListMaker<>();
List<String> list = stringMaker.create("Hello", 4);
System.out.println(list);
}
}
擦除的补偿
擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都无法总做!
package com.generic;
public class Erased<T> {
private final int SIZE = 100;
public void f(Object arg){
/*if(arg instanceof T){
}
T var = new T();
T[] array = new T[SIZE];*///报错
T[] array = (T[])new Object[SIZE];//警告
}
}
绕过这些问题来编程,可以使用Class:
package com.generic;
class Building{}
class House extends Building{}
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind){
this.kind = kind;
}
public boolean f(Object arg){
return kind.isInstance(arg);
}
public static void main(String[] args) {
ClassTypeCapture<Building> ctt = new ClassTypeCapture<>(Building.class);
System.out.println(ctt.f(new Building()));
System.out.println(ctt.f(new House()));
ClassTypeCapture<House> ctt2 = new ClassTypeCapture<>(House.class);
System.out.println(ctt2.f(new Building()));
System.out.println(ctt2.f(new House()));
}
}
这样,可以确保类型标签可以匹配泛型参数。
个人理解:
泛型 T 只可以用来标识另一种东西,如容器类型,返回值类型,参数类型等,但不可以像类一样生成对象!即不能用在运行时。
new T[]这样的操作无法实现部分原因是因为擦除,而另一部分原因是因为编译器不能检查T具有默认构造器这里写代码片
,下面代码将解决这一问题:
package com.generic;
class ClassAsFactory<T>{
public T x;
public ClassAsFactory(Class<T> kind){
try {
x = kind.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Employee{
public void print(){
System.out.println("Employee");
}
}
public class InstantiateGenericType {
public static void main(String[] args) {
ClassAsFactory<Employee> fe = new ClassAsFactory<>(Employee.class);
fe.x.print();
ClassAsFactory<Integer> in = new ClassAsFactory<>(Integer.class);
}
}
可以看到生成ClassAsFactory<Integer>
实例时会报错,因为Integer没有默认的构造器。同时,这个错误不是在编译期捕获的。
使用显示的工厂方法避免运行时异常,个人认为这种方法不太灵活。
package com.generic;
interface Factory<T>{
T create();
}
class Foo2<T>{
private T x;
public <F extends Factory<T>> Foo2(F factory){
x = factory.create();
}
}
class IntegerFactory implements Factory<Integer>{
public Integer create(){
return new Integer(0);
}
}
class Widght{
public static class FactoryDemo implements Factory<Widght>{
public Widght create(){
return new Widght();
}
}
}
public class FactoryConstraint {
public static void main(String[] args) {
new Foo2<Integer>(new IntegerFactory());
new Foo2<Widght>(new Widght.FactoryDemo());
}
}
使用模板方法模式也能解决这个问题:
package com.generic;
abstract class GenericWithCreate<T>{
final T ele;
GenericWithCreate(){
ele = create();
}
abstract T create();
}
class X{}
class Creator extends GenericWithCreate<X>{
X create(){
return new X();
}
void f(){
System.out.println(ele.getClass().getSimpleName());
}
}
public class CreatorGeneric {
public static void main(String[] args) {
Creator creator = new Creator();
creator.f();
}
}
泛型数组
如前面所示,不能创建泛型数组一般的解决档案是使用ArrayList:
package com.generic;
import java.util.*;
public class ArrayOfGenericReference<T> {
private List<T> array = new ArrayList<T>();
public void add(T item){
array.add(item);
}
public T get(int index){
return array.get(index);
}
}
package com.generic;
import java.lang.reflect.Array;
class Generic<T>{}
public class ArrayOfGeneric {
static final int SIZE = 5;
static Generic<Integer>[] gia;
public static void main(String[] args) {
gia = (Generic<Integer>[])new Generic[SIZE];
System.out.println(gia.getClass().getSimpleName());
gia[0] = new Generic<Integer>();
//gia[1] = new Generic<Double>();报错
for(int i = 0; i < SIZE; i++){
System.out.println(gia[i]);
}
}
}
挺正常的代码。