《JAVA编程思想》这本书确实是非常的晦涩难懂,所以我决定博客也跟着阅读进度一起更新,今天的阅读是无界通配符,废话不多说,来看下面的代码:
import java.util.ArrayList;
import java.util.List;
public class UnboundedWildcards1
{
static List list1;
/* 警告:List使用的是原始类型,但是List1是一个泛型容器
* 所以此时应该使用泛型*/
static List<?> list2;
static List<? extends Object> list3;
static void assign1(List list)/*报错信息和第5行一样*/
{
list1 = list;
list2 = list;
//list3 = list;
/* 警告:
* 泛型转换错误,这个时候只要List由原始类型转换为
* 任何泛型都可以。*/
}
static void assign2(List<?> list)
{
list1 = list;
list2 = list;
list3 = list;
/* 我觉得这个地方list3 = list并没有报错的原因还是因为
* 类型擦除的原因*/
}
static void assign3(List<? extends Object> list)
{
list1 = list;
list2 = list;
list3 = list;
/*不报错原因和assign2方法中解释的原因一样*/
}
public static void main(String[] args)
{
assign1(new ArrayList());
/* 警告:
* 1.ArrayList并不能转化成类型或者容器
* 2.ArrayList需要配合泛型进行使用*/
assign2(new ArrayList());
/*警告:警告内容同上*/
//assign3(new ArrayList());
/* 警告:
* 泛型转换异常,ArrayList虽然可以转化为List和List<?>
* 但是由于assign3方法中参数要求转化为
* List<? extends Object>所以这个时候转型错误*/
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>());
//以上所有泛型都可以转化为<?>从而被所有接受
List<?> wildList = new ArrayList();
wildList = new ArrayList<String>();
assign1(wildList);
assign2(wildList);
assign3(wildList);
}
}
从这段代码可以看出,
List<?> a1 = new ArrayList<>();
List<? extends Object> a2 = new ArrayList<>();
a2 = a1;//ok
List<? extends Object> a3 = new ArrayList<>();
a2 = a3;//ok
但是可以从第一段代码可以看出,两者对于原始类型的转化严格程度是不同的,看下面这段代码:
import java.util.*;
//import static net.mindview.util.Print.*;
class BerylliumSphere{}
public class ArrayOfGenericType
{
public static void main(String[] args){
List a0 = new ArrayList();
List<Object> a1 = new ArrayList<>();
List<?> a2 = new ArrayList<>();
List<? extends Object> a3 = new ArrayList<>();
List<? super Object> a4 = new ArrayList<>();
a0 = a1;
a0 = a2;
a0 = a3;
a0 = a4;
//可见原型容器可以接受所有参数类容器的转型
a1 = a0;
a1 = a2;//List<?>不能转换位List<Object>
a1 = a3;//List<? extends Object>不能转换位List<Object>
a1 = a4;
a2 = a0;//这个地方并没有出现警告
a2 = a1;
a2 = a3;
a2 = a4;
a3 = a0;//警告:在未检查的情况下进行泛型转换
a3 = a1;
a3 = a2;
a3 = a4;
a4 = a0;//警告:在未检查的情况下进行泛型转换
a4 = a1;
a4 = a2;//List<?>不能转型为List<? super Object>,范围扩大
a4 = a3;//List<? extends Object>不能转型为List<? super Object>,范围扩大
a0.add(new Object());
a1.add(new Object());
a2.add(new Object());//编译错误,无解通配符实际范围要小于Object
a3.add(new Object());//编译错误,? extends Object不能将Object作为参数是因为泛型的范围小于Object
a4.add(new Object());
}
}
可以看出 ? extends Object 相比较于 ? 对于原始类型的要求更加严格。
接下来再看一段代码
import java.util.*;
@SuppressWarnings("rawtypes")
public class unboundedWildcards2
{
static Map map1;
static Map<?,?> map2;
static Map<String, ?> map3;
static void assign1(Map map){map1 = map;}
static void assign2(Map<?,?> map){map2 = map;}
static void assign3(Map<String, ?> map){map3 = map;}
public static void main(String[] args)
{
assign1(new HashMap());
assign2(new HashMap());
//assign3(new HashMap());
/* 警告:
* 因为这个地方第一个泛型指明了参数String,所以
* 这个时候用原始类型进行转换就会报错*/
assign1(new HashMap<String,Integer>());
assign2(new HashMap<String,Integer>());
assign3(new HashMap<String,Integer>());
}
}
从上面的代码可以看出,如果指定了泛型那么泛型就必须符合指定的泛型。
下面用一大段代码总结一下无界通配符这一个小节:
import java.util.*;
class Holder<T>
{
private T value;
public Holder() {}
public Holder(T val){ value = val;}
public void set(T arg){ value = arg;}
public T get(){return value;}
public boolean equals(Object obj)
{
return value.equals(obj);
//Object的.equals方法是判断对象是否相同,而不是值
}
}
@SuppressWarnings({"unused","rawtypes"})
public class Wildcards
{
static void rawArgs(Holder holder, Object arg)
{
//holder.set(arg);
/* 警告:
* 警告的原因是holder是一个泛型容器,但是这个地方用的
* 是原始类型,导致类型不正确*/
//holder.set(new Wildcards());
// 警告:原因同上
//T t = holder.get();
// 错误:在个方法中并不存在泛型
Object obj = holder.get();
//***虽然Holder.get()返回的是一个泛型,但是仍然可以转换
//***为Object类型
}
static void unboundedArg(Holder<?> holder, Object arg)
{
//holder.set(arg);
/* 警告:由于Holder是一个泛型容器,这个时候使用了无
* 界通配符将导致Object不能作为.set()方法的参数*/
//holder.set(new Wildcards());
// 警告:原因同上
//T t = holder.get();
// 错误:不存在T作为类型
Object obj = holder.get();
}
static <T> T exact1(Holder<T> holder)
{
T t = holder.get();
return t;
}
//*** .get()泛型方法+泛型容器将不会报错
// 如果将参数中的Holder<T>改为Holder将导致报错,并且
// 错误原因为.get()方法的返回值为Object,不能将Object转化
// 为T类型
static <T> T exact2(Holder<T> holder,T arg)
{
holder.set(arg);
T t = holder.get();
return t;
}
/* 想要泛型容器Holder的.set()方法不报错,要注意:
* .set()方法的参数要求为泛型,并且其参数的范围应该要
* 小于Holder容器的泛型参数的范围,否则将报错,比如:
* 如果arg的泛型为<Object>但holder的为<? extends Object>
* 就会导致报错,但是如果arg的泛型为<Object>但是holder的为
* <? super Object>就不会报错,但是这样的话.get()方法
* 就会报错*/
static <T> T wildSubtype(Holder<? extends T>holder, T arg)
{
//holder.set(arg);
//* 错误:原因同上,holder的泛型范围不能小于arg的范围
T t = holder.get();
return t;
}
static <T> void wildSupertype(Holder<? super T> holder,T arg)
{
holder.set(arg);
//T t = holder.get();
//* 错误:原因同上,holder的范围大于t,不能向下转型
Object t = holder.get();
//虽然这个不会报错,但是已经失去了类型信息
}
public static void main(String[] args)
{
Holder raw = new Holder<Long>();
//或者
raw = new Holder();
Holder<Long> qualified = new Holder<Long>();
Holder<?> unbounded = new Holder<Long>();
Holder<? extends Long> bounded = new Holder<Long>();
Holder<? super Long> spbounded = new Holder<Long>();
Long lng = 1L;
rawArgs(raw,lng);
rawArgs(qualified, lng);
rawArgs(unbounded, lng);
rawArgs(bounded, lng);
rawArgs(spbounded, lng);
/* 由上面的结果可以看出不管holder是怎么样的泛型参数
* 都可以由原型Holder来进行引用*/
unboundedArg(raw, lng);
unboundedArg(qualified, lng);
unboundedArg(unbounded, lng);
unboundedArg(bounded, lng);
rawArgs(spbounded, lng);
/* 看来不仅是原型Holder可以引用泛型holder,holder<?>
* 也可以引用带各种泛型的holder*/
//Object r1 = exact1(raw);
/* 警告:1.exact1方法需要的是泛型的holder容器,不能
* 使用原型容器 2.raw所引用的原型容器在没有检查的情况
* 下转换成了泛型容器*/
Long r2 = exact1(qualified);
Object r3 = exact1(unbounded);
Long r4 = exact1(bounded);
//Long r5 = exact1(spbounded);
/* 错误:此时返回值是Long的超集,故不能用其子集表示它*/
Long r6 = exact2(raw, lng);
/* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
* Holder容器而不是一个原型 2.raw所引用的原型容器在
* 没有进行检查的情况下进行了转换*/
Long r7 = exact2(qualified, lng);
//Long r8 = exact2(unbounded, lng);
/* 错误:可以理解为这个时候unbounded使用的无界通配符
* 的范围大于exact2()中的泛型T所以此时报错*/
//Long r9 = exact2(bounded, lng);
/* 错误:理由同上*/
Object r10 = exact2(spbounded, lng);
//Long r11 = wildSubtype(raw, lng);
/* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
* Holder容器而不是一个原型 2.raw所引用的原型容器在
* 没有进行检查的情况下进行了转换*/
Long r12 = wildSubtype(qualified, lng);
Object r13 = wildSubtype(unbounded, lng);
Long r14 = wildSubtype(bounded, lng);
Object r15 = wildSubtype(spbounded, lng);
//由上面的情况可以看出<? extends T>这种泛型可以减少泛
//型所带来的烦恼
//wildSupertype(raw, lng);
/* 警告:原因是:1.参数不正确,exact2()方法需要一个泛型
* Holder容器而不是一个原型 2.raw所引用的原型容器在
* 没有进行检查的情况下进行了转换*/
wildSupertype(qualified, lng);
//wildSupertype(unbounded, lng);
/* 这是我觉得非常特殊的一个情况,可能是泛型设计是的一个
* 错误,看下面的代码:
* List<? super Object> l1 = new ArrayList<Object>();
* List<?> l2 = new ArrayList<Object>();
* l1 = l2;//错误
* 看来在不管什么情况下就算是使用了super都不能进行引用
* 但是<?>和<? extends Object>之间却可以很好的转化*/
//wildSupertype(bounded, lng);
wildSupertype(spbounded, lng);
}
}