Java编程思想---泛型(3)
泛型带来的各类问题
任何基本类型都不能作为类型参数
Java泛型的限制之一是不能讲基本类型作为类型参数。
解决方案是JDK的自动装箱拆箱机制
如;
public class ListOfInt {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(i);
}
for (int i : list){
System.out.println(i);
}
}
}
自动包装不能解决所有问题:
public class FArray {
public static <T> T[] fill(T[] a, Generator<T> generator){
for (int i = 0; i < a.length; i++) {
a[i] = generator.next();
}
return a;
}
}
public class PrimitiveGenericTest {
public static void main(String[] args) {
String[] strings = FArray.fill(
new String[7],new RandomGenerator.String(10)
);
for (String s:strings){
System.out.println(s);
}
Integer[] integers = FArray.fill(
new Integer[7],new RandomGenerator.Integer()
);
for(int i:integers){
System.out.println(i);
}
// 自动装箱在这里不会起作用了
// int[] b = FArray.fill(new int[7],new RandomGenerator());
}
}
可以看到,自动包装机制不能用于数组
参数化接口
一个类不能实现同一个泛型接口的两种变体
public interface Apple<T> {
}
public class ReadF implements Apple<ReadF>{
}
//public class ReadF2 extends ReadF implements Apple<ReadF2>{
//error
//}
ReadF2无法编译,因为擦差会将Apple< ReadF >与Apple< Read2F >简化为一样的类Apple,上面的代码就意味着重复两次实现相同的接口。
转型与警告
使用带有泛型类型参数的转型或instanceof不会有任何效果
public class FixedSizeStack<T> {
private int index = 0;
private Object[] storage;
public FixedSizeStack(int size) {
storage = new Object[size];
}
public void push(T item){
storage[index++] = item;
}
public T pop(){
return (T) storage[--index];
}
}
public class GenericCast {
public static final int size = 10;
public static void main(String[] args) {
FixedSizeStack<String> fixedSizeStack =
new FixedSizeStack<>(size);
for(String s : "A B J J I D U K L P".split(" ")){
fixedSizeStack.push(s);
}
for (int i = 0; i < size; i++) {
String s = fixedSizeStack.pop();
System.out.println(s +" ");
}
}
}
编译器会对
return (T) storage[--index];
发出警告,这是因为擦除的原因,编译器无法知道这个转型是否是安全的,并且pop实际上没有执行任何转型
重载
public class UseList<P,Q> {
// void f(List<P> p){
//
// }
//
// void f(List<Q> q){
//
// }
}
上面例子由于擦除的原因,重载方法将产生相同的类型签名
自限定的类型
令人费解的用法:
public class SelfBounded<T extends SelfBounded<T>> {
}
SelfBounded类接受泛型参数T,而T由一个边界类限定,这个边界就是拥有T作为其参数的SelfBounded
例子:
public class BasicHolder<T> {
T element;
public void setElement(T element) {
this.element = element;
}
public T getElement() {
return element;
}
void f(){
System.out.println(element.getClass().getSimpleName());
}
}
public class Subtype extends BasicHolder<Subtype>{
}
public class CRGWithBasicHolder {
public static void main(String[] args) {
Subtype subtype = new Subtype();
Subtype subtype2 = new Subtype();
subtype.setElement(subtype2);
Subtype subtype3 = subtype.getElement();
subtype.f();
}
}
细节:
新类Subtype接受的参数和返回的值具有Subtype类型而不仅仅是基类BasicHolder类型,这就是CRG的本质:基类用导出类替代其参数
BasicHolder可以使用任何类型作为其泛型参数
public class Other {
}
public class BasicOther extends BasicHolder<Other>{
}
public class UnconStrained {
public static void main(String[] args) {
BasicOther b = new BasicOther();
BasicOther b2 = new BasicOther();
b.setElement(new Other());
Other other = b.getElement();
b.f();
}
}
参数协变
自限定类型的价值在于它们可以产生协变参数类型---方法参数类型会随子类而变化
public class Apple extends Fruit{
@Override
public String toString() {
return "Apple";
}
}
public class Fruit {
}
public interface GetApple extends GetFruit{
Apple get();
}
public interface GetFruit {
Fruit get();
}
public class Test {
static void test(GetApple getApple){
Apple apple = getApple.get();
}
public static void main(String[] args) {
}
}
GetApple中的get方法覆盖了GetFruit中的get(),并且返回了一个从GetFruit.get()的返回类型中导出的类型。
混型
public interface Fly {
void fly();
}
public class Swing implements Fly{
@Override
public void fly() {
System.out.println("I can Fly");
}
}
public class Duck implements Fly{
private Swing swing = new Swing();
@Override
public void fly() {
swing.fly();
}
void swim(){
System.out.println("I can swim");
}
}
但是当使用更复杂的模型时,代码数量会急剧增加
装饰器
public interface Basket {
void show();
}
public class Original implements Basket{
@Override
public void show() {
System.out.println("I am Basket");
}
}
public class AppleDecorator implements Basket{
private Basket basket;
public AppleDecorator(Basket basket) {
this.basket = basket;
}
@Override
public void show() {
basket.show();
System.out.println("APPLE");
}
}
public class BananaDecorator implements Basket{
private Basket basket;
public BananaDecorator(Basket basket) {
this.basket = basket;
}
@Override
public void show() {
basket.show();
System.out.println("BANANA");
}
}
public class Main {
public static void main(String[] args) {
Original original = new Original();
Basket basket =
new AppleDecorator(new BananaDecorator(original));
basket.show();
}
}
潜在类型机制
在C++和python中,可以不知道当前类的类型,就可以调用方法
Java如何进行补偿呢?
反射
public class TypeOne {
private int data = 1;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
public class TypeTwo {
private int data = 2;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
}
public class Main1 {
public static void getData(Object type){
Class<?> c = type.getClass();
try {
Method method = c.getMethod("getData");
int data = (int) method.invoke(type,null);
System.out.println(type.getClass().getSimpleName()+" data:"+data);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TypeOne one = new TypeOne();
TypeTwo typeTwo = new TypeTwo();
getData(one);
getData(typeTwo);
}
}
将一个方法用于序列
public class Full {
public static <T,S extends Iterable<T>> void fill(
S s, Method method,Object...args
){
Iterator<T> iterator = s.iterator();
while (iterator.hasNext()){
T t = iterator.next();
try {
method.invoke(t,args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
public class Apple {
private static int count = 1;
private final int id = count++;
public void getId(){
System.out.println(id);
}
}
public class Main2 {
public static void main(String[] args) {
ArrayList<Apple> list = new ArrayList<>();
for(int i=0; i<10; ++i){
list.add(new Apple());
}
try {
fill(list, Apple.class.getMethod("getId"),null);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
}
}
网友评论