写在之前
以下是《Java8编程入门官方教程》中的一些知识,如有错误,烦请指正。涉及的程序如需下载请移步http://down1.tupwk.com.cn/qhwkdownpage/978-7-302-38738-1.zip
泛型
泛型的核心概念就是参数化模型。参数化模型使程序员能创建将要操作的数据类型作为形参的类、接口、和方法。操作类型形参的类、接口和方法就称为泛型。
//简单的泛型类型参数,当建立Gen对象时,T会被实参取代
//建立一个泛型类,T是泛型类型形参
class Gen<T> {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
// Show type of T.
void showType() {
System.out.println("Type of T is " +
ob.getClass().getName());
}
}
// Demonstrate the generic class.
class GenDemo {
public static void main(String args[]) {
//创建Gen<Integers>的引用.
Gen<Integer> iOb;
// Create a Gen<Integer> object and assign its
// reference to iOb. Notice the use of autoboxing
// to encapsulate the value 88 within an Integer object.
iOb = new Gen<Integer>(88); ////实例化Gen<Integers>的
// Show the type of data used by iOb.
iOb.showType();
// Get the value in iOb. Notice that
// no cast is needed.
int v = iOb.getob();
System.out.println("value: " + v);
System.out.println();
// Create a Gen object for Strings.
Gen<String> strOb = new Gen<String>("Generics Test");
// Show the type of data used by strOb.
strOb.showType();
// Get the value of strOb. Again, notice
// that no cast is needed.
String str = strOb.getob();
System.out.println("value: " + str);
}
}
泛型只能用于引用类型,当声明泛型类型的实例时,传递给类型形参的类型实参必须是类类型。不能使用基本类型。
Gen<int> intOb = new Gen<int>(88);//错误演示
但是可以使用类型封装器来封装基本类型。
在来看一个带有两个类型形参的泛型类。
class TwoGen<T, V> {
T ob1;
V ob2;
// Pass the constructor a reference to
// an object of type T.
TwoGen(T o1, V o2) {
ob1 = o1;
ob2 = o2;
}
// Show types of T and V.
void showTypes() {
System.out.println("Type of T is " +
ob1.getClass().getName());
System.out.println("Type of V is " +
ob2.getClass().getName());
}
T getob1() {
return ob1;
}
V getob2() {
return ob2;
}
}
// Demonstrate TwoGen.
class SimpGen {
public static void main(String args[]) {
TwoGen<Integer, String> tgObj =
new TwoGen<Integer, String>(88, "Generics");
// Show the types.
tgObj.showTypes();
// Obtain and show values.
int v = tgObj.getob1();
System.out.println("value: " + v);
String str = tgObj.getob2();
System.out.println("value: " + str);
}
}
约束类型
指定类型形参时,可以创建一个上层约束来声明一个超类,所有类型实参必须继承该超类。
class NumericFns<T extends Number> { //类型实参必须是Number或者Number的子类
T num;
// Pass the constructor a reference to
// a numeric object.
NumericFns(T n) {
num = n;
}
// Return the reciprocal.
double reciprocal() {
return 1 / num.doubleValue();
}
// Return the fractional component.
double fraction() {
return num.doubleValue() - num.intValue();
}
// ...
}
// Demonstrate NumericFns.
class BoundsDemo {
public static void main(String args[]) {
NumericFns<Integer> iOb =
new NumericFns<Integer>(5);
System.out.println("Reciprocal of iOb is " +
iOb.reciprocal());
System.out.println("Fractional component of iOb is " +
iOb.fraction());
System.out.println();
NumericFns<Double> dOb =
new NumericFns<Double>(5.25);
System.out.println("Reciprocal of dOb is " +
dOb.reciprocal());
System.out.println("Fractional component of dOb is " +
dOb.fraction());
// This won't compile because String is not a
// subclass of Number.
// NumericFns<String> strOb = new NumericFns<String>("Error");
}
}
通配符实参
NumericFns<?>
表示匹配任何类型的NumericFns
对象,从而允许任意两个NumericFns
对象比较其绝对值。
class NumericFns<T extends Number> {
T num;
// Pass the constructor a reference to
// a numeric object.
NumericFns(T n) {
num = n;
}
// Return the reciprocal.
double reciprocal() {
return 1 / num.doubleValue();
}
// Return the fractional component.
double fraction() {
return num.doubleValue() - num.intValue();
}
// Determine if the absolute values of two
// objects are the same.
boolean absEqual(NumericFns<?> ob) {
if(Math.abs(num.doubleValue()) ==
Math.abs(ob.num.doubleValue())) return true;
return false;
}
// ...
}
// Demonstrate a wildcard.
class WildcardDemo {
public static void main(String args[]) {
NumericFns<Integer> iOb =
new NumericFns<Integer>(6);
NumericFns<Double> dOb =
new NumericFns<Double>(-6.0);
NumericFns<Long> lOb =
new NumericFns<Long>(5L);
System.out.println("Testing iOb and dOb.");
if(iOb.absEqual(dOb))
System.out.println("Absolute values are equal.");
else
System.out.println("Absolute values differ.");
System.out.println();
System.out.println("Testing iOb and lOb.");
if(iOb.absEqual(lOb))
System.out.println("Absolute values are equal.");
else
System.out.println("Absolute values differ.");
}
}
约束通配符
通配符实参可以像类型实参一样被约束。
class UseBoundedWildcard {
// Here, the ? will match A or any class type that
// that extends A
static void test(Gen<? extends A> o) { //使用通配符
// ...
}
public static void main(String args[]) {
A a = new A();
B b = new B();
C c = new C();
D d = new D();
Gen<A> w = new Gen<A>(a);
Gen<B> w2 = new Gen<B>(b);
Gen<C> w3 = new Gen<C>(c);
Gen<D> w4 = new Gen<D>(d);
// These calls to test() are OK.
test(w);
test(w2);
test(w3);
// Can't call test() with w4 because
// it is not an object of a class that
// inherits A.
// test(w4); // Error!
}
}
泛型方法
可以创建不属于泛型类的泛型方法。
class GenericMethodDemo {
// Determine if the contents of two arrays are same.
static <T extends Comparable<T>, V extends T> boolean arraysEqual(T[] x, V[] y) { //泛型方法
// If array lengths differ, then the arrays differ.
if(x.length != y.length) return false;
for(int i=0; i < x.length; i++)
if(!x[i].equals(y[i])) return false; // arrays differ
return true; // contents of arrays are equivalent
}
public static void main(String args[]) {
Integer nums[] = { 1, 2, 3, 4, 5 };
Integer nums2[] = { 1, 2, 3, 4, 5 };
Integer nums3[] = { 1, 2, 7, 4, 5 };
Integer nums4[] = { 1, 2, 7, 4, 5, 6 };
if(arraysEqual(nums, nums))
System.out.println("nums equals nums");
if(arraysEqual(nums, nums2))
System.out.println("nums equals nums2");
if(arraysEqual(nums, nums3))
System.out.println("nums equals nums3");
if(arraysEqual(nums, nums4))
System.out.println("nums equals nums4");
// Create an array of Doubles
Double dvals[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
// This won't compile because nums and dvals
// are not of the same type.
// if(arraysEqual(nums, dvals))
// System.out.println("nums equals dvals");
}
}
泛型构造函数
即使其类不是泛型的,构造函数也可以是泛型的。
class Summation {
private int sum;
<T extends Number> Summation(T arg) { //
sum = 0;
for(int i=0; i <= arg.intValue(); i++)
sum += i;
}
int getSum() {
return sum;
}
}
class GenConsDemo {
public static void main(String args[]) {
Summation ob = new Summation(4.0);
System.out.println("Summation of 4.0 is " +
ob.getSum());
}
}
泛型接口
interface Containment<T> {
// The contains() method tests if a
// specific item is contained within
// an object that implements Containment.
boolean contains(T o);
}
// Implement Containment using an array to
// hold the values.
class MyClass<T> implements Containment<T> {
T[] arrayRef;
MyClass(T[] o) {
arrayRef = o;
}
// Implement Contains.
public boolean contains(T o) {
for(T x : arrayRef)
if(x.equals(o)) return true;
return false;
}
}
class GenIFDemo {
public static void main(String args[]) {
Integer x[] = { 1, 2, 3 };
MyClass<Integer> ob = new MyClass<Integer>(x);
if(ob.contains(2))
System.out.println("2 is in ob");
else
System.out.println("2 is NOT in ob");
if(ob.contains(5))
System.out.println("5 is in ob");
else
System.out.println("5 is NOT in ob");
// The follow is illegal because ob
// is an Integer Containment and 9.25 is
// a Double value.
// if(ob.contains(9.25)) // Illegal!
// System.out.println("9.25 is in ob");
}
}
如果一个类实现泛型接口的特定类型,实现接口的类不需要是泛型的。如下:
class MyClass implements Containments<Double>{//....}
泛型接口指定的类型形参可以被约束
interface Containment<T extends Number>
此时,实现该接口的类需要传递一个相同约束的类型实参。
class MyClass<T entends Number> implements Containment<T> //一旦接口建立了约束,不需要再implements子句中再次指定
来看一个综合应用
// A generic queue interface.
public interface IGenQ<T> {
// Put an item into the queue.
void put(T ch) throws QueueFullException;
// Get an item from the queue.
T get() throws QueueEmptyException;
}
// An exception for queue-full errors.
public class QueueFullException extends Exception {
int size;
QueueFullException(int s) { size = s; }
public String toString() {
return "\nQueue is full. Maximum size is " +
size;
}
}
public class QueueEmptyException extends Exception {
public String toString() {
return "\nQueue is empty.";
}
}
class GenQueue<T> implements IGenQ<T> {
private T q[]; // this array holds the queue
private int putloc, getloc; // the put and get indices
// Construct an empty queue with the given array.
public GenQueue(T[] aRef) {
q = aRef;
putloc = getloc = 0;
}
// Put an item into the queue.
public void put(T obj)
throws QueueFullException {
if(putloc==q.length)
throw new QueueFullException(q.length);
q[putloc++] = obj;
}
// Get a character from the queue.
public T get()
throws QueueEmptyException {
if(getloc == putloc)
throw new QueueEmptyException();
return q[getloc++];
}
}
class GenQDemo {
public static void main(String args[]) {
// Create in integer queue.
Integer iStore[] = new Integer[10];
GenQueue<Integer> q = new GenQueue<Integer>(iStore);
Integer iVal;
System.out.println("Demonstrate a queue of Integers.");
try {
for(int i=0; i < 5; i++) {
System.out.println("Adding " + i + " to q.");
q.put(i); // add integer value to q
}
}
catch (QueueFullException exc) {
System.out.println(exc);
}
System.out.println();
try {
for(int i=0; i < 5; i++) {
System.out.print("Getting next Integer from q: ");
iVal = q.get();
System.out.println(iVal);
}
}
catch (QueueEmptyException exc) {
System.out.println(exc);
}
System.out.println();
// Create a Double queue.
Double dStore[] = new Double[10];
GenQueue<Double> q2 = new GenQueue<Double>(dStore);
Double dVal;
System.out.println("Demonstrate a queue of Doubles.");
try {
for(int i=0; i < 5; i++) {
System.out.println("Adding " + (double)i/2 +
" to q2.");
q2.put((double)i/2); // add double value to q2
}
}
catch (QueueFullException exc) {
System.out.println(exc);
}
System.out.println();
try {
for(int i=0; i < 5; i++) {
System.out.print("Getting next Double from q2: ");
dVal = q2.get();
System.out.println(dVal);
}
}
catch (QueueEmptyException exc) {
System.out.println(exc);
}
}
}
原类型
为了处理向泛型过渡的问题,Java允许不带任何类型实参使用泛型类。这样做将创建一个原类型的类,原类型与不识别泛型的代码兼容。使用原类型的缺点是丧失了泛型的类型安全性。
// Demonstrate a raw type.
class Gen<T> {
T ob; // declare an object of type T
// Pass the constructor a reference to
// an object of type T.
Gen(T o) {
ob = o;
}
// Return ob.
T getob() {
return ob;
}
}
// Demonstrate raw type.
class RawDemo {
public static void main(String args[]) {
// Create a Gen object for Integers.
Gen<Integer> iOb = new Gen<Integer>(88);
// Create a Gen object for Strings.
Gen<String> strOb = new Gen<String>("Generics Test");
// Create a raw-type Gen object and give it
// a Double value.
Gen raw = new Gen(new Double(98.6)); //不提供类型实参时,创建原类型
// Cast here is necessary because type is unknown.
double d = (Double) raw.getob();
System.out.println("value: " + d);
// The use of a raw type can lead to run-time.
// exceptions. Here are some examples.
// The following cast causes a run-time error!
// int i = (Integer) raw.getob(); // run-time error
// This assigment overrides type safety.
strOb = raw; // OK, but potentially wrong
// String str = strOb.getob(); // run-time error
// This assingment also overrides type safety.
raw = iOb; // OK, but potentially wrong
// d = (Double) raw.getob(); // run-time error
}
}
类型推断
JDK7之后引入了类型推断。
TwoGen<Integer, String> tgObj = new TwoGen<Integer, String>(88, "Generics"); //完整形式
TwoGen<Integer, String> tgObj = new TwoGen<>(88, "Generics");
歧义
重载的时候类型的不同可能会不成立。这个时候就会引起歧义。
泛型限制
- 不能创建类型形参的实例
class Gen<T> {
T ob;
Gen() {
ob = new T(); // Illegal!!!
}
}
- 静态成员不能使用包含类声明的类型形参
class Wrong<T> {
// Wrong, no static variables of type T.
static T ob;
// Wrong, no static method can use T.
static T getob() {
return ob;
}
// Wrong, no static method can access object
// of type T.
static void showob() {
System.out.println(ob);
}
}
- 泛型数组限制
不能实例化基类型为类型形参的数组;不能创建特定类型泛型引用的数组
class Gen<T extends Number> {
T ob;
T vals[]; // OK
Gen(T o, T[] nums) {
ob = o;
// This statement is illegal.
// vals = new T[10]; // can't create an array of T
// But, this statement is OK.
vals = nums; // OK to assign reference to existent array
}
}
-
泛型异常限制
不能扩展
Throwable
,不能创建泛型异常类。