转载

java – 泛型super/extends小练手

下面通过一个简短有说明价值的小例子,来记录一下JAVA泛型中super、extends的用法和区别。

项目地址: https://github.com/owenliang/java-somewhat/tree/master/src/cc/yuerblog/template 。

Vector泛型容器定义

package cc.yuerblog.template;
 
import java.util.ArrayList;
 
/**
 * 叫做Vector的数组容器
 * @author liangdong
 *
 * @param <T>
 */
public class Vector<T> extends ArrayList<T> {
	void walk(Walker<? super T> walker) {
		for (int i = 0; i < this.size(); ++i) {
			walker.onValue(this.get(i));
		}
	}
}

为了保持简洁,Vector类直接继承自ArrayList,也就拥有了和ArrayList同样的方法,同时Vector也是一个泛型类。

定义泛型类只需要用到<T>这样的语法,不会再复杂了。

walk方法的本意是要求用户传入一个实现了Walker接口的对象,以便可以得到vector的元素遍历回调。

但是walk方法在声明函数参数的时候会用到更加复杂的泛型语法:<? super T>,它的意思是要求Walker接口必须能够接受T类型或者T类型的父类型作为回调参数,什么意思呢?

如果是Vector<Integer>对象,那么它不仅可以接受Walker<Integer>作为参数,甚至可以将Walker<Number>作为参数,这是因为Integer可以向上转型为Number,所以Walker<Number>同样可以兼容。

我们急需看一下Walker接口的定义。

Walker接口定义

package cc.yuerblog.template;
 
/**
 * 用于遍历Vector的回调处理类
 * @author liangdong
 *
 * @param <T>
 */
public interface Walker<T> {
	/**
	 * 遍历回调函数
	 * @param value
	 */
	void onValue(T value);
}

该接口也是泛型的,从而可以支持任何元素类型。

onValue方法没有什么魔法,就是<T>决定的。

实现Walker接口并作为回调

现在我们在创建Vector<Integer>对象,实现Walker<Number>接口,然后结合<? super T>看一下整体是如何工作的:

package cc.yuerblog.template;
 
public class Main {
	public static void main(String[] args) {
		Vector<Integer> vector = new Vector<>();
		
		vector.add(1);
		vector.add(2);
		
		// 基于callback遍历:遍历vector, 要求回调函数能兼容Integer向上转型即可
		vector.walk(new Walker<Number>() {
			@Override
			public void onValue(Number value) {
				System.out.println(value.intValue());
			}
		});
		
		// 直接遍历:要求Vector中元素是Number的子类
		Main.walk(vector);
	}

创建vector时,JAVA可以根据左侧变量类型自动推断,所以右侧只需要一个空的<>即可。

接下来传入了一个Walker<Number>接口的实现,覆写的是onValue(Number value)方法。

这时候我们再去看Vector<Integer>::walk方法的实现:

	void walk(Walker<? super T> walker) {
		for (int i = 0; i < this.size(); ++i) {
			walker.onValue(this.get(i));
		}
	}

此时walk方法的参数是:Walker<? super Integer> walker,表示可以接纳Walker<Integer>或者父类的Walker<Number>。

onValue(Number value)回调函数Integer类型的元素,是可以完成自动向上转换的,这也是为什么Vector<T>的walk方法应该用super的原因:即只要回调函数能经过向上转型兼容传入的参数即可,Vector没必要要求Walker一定要用T类型,父类也是可以的。

再看一下extends

extends的出发点则不同,我们先看代码:

public class Main {
	public static void main(String[] args) {
		Vector<Integer> vector = new Vector<>();
		
		vector.add(1);
		vector.add(2);
		
		// 直接遍历:要求Vector中元素是Number的子类
		Main.walk(vector);
	}
	
	public static void walk(Vector<? extends Number> vector) {
		for (int i = 0; i < vector.size(); ++i) {
			Number num = vector.get(i);
			System.out.println(num.intValue());
		}
	}
}

简单说,我定义了一个Main.walk方法,希望支持任意继承自Number的元素类型进行遍历。

所以这里函数参数的声明是Vector<? extends Number>,即:vector里的元素只要是Number的子类即可,我都可以接受,并且我在处理的时候都可以安全的向上转型为Number处理。

总结

JAVA定义泛型类只需要简单<T>,而声明函数参数的时候就出现了extends和super的两种视角,理解它们的意图应该是最重要的,上面的例子给了一个很好的说明。

如果文章帮到了你,请您乐于扫码捐赠1元钱,以便支持服务器运转。

java – 泛型super/extends小练手
原文  https://yuerblog.cc/2019/08/09/java-泛型super-extends小练手/
正文到此结束
Loading...