2

《? extends T与? super T》之后,我们再聊聊泛型。

Demo 1

public interface Generator {
    <T> T next();
}

第一种解决方法,在方法返回类型前加“<T>”使其成为一个泛型方法。

public interface Generator<T> {
    T next();
}

第二种解决方法,在接口名后加“<T>”使其成为一个泛型接口。

泛型类/接口在使用前,必须先指名参数类型,其中除了泛型方法外的泛型参数都将是所指定的类型。如下:

// 泛型接口与其子类
public interface Generator<T> {
    T next(T t);
    void print(T t);
}

public class SubGenerator implements Generator<String>{

    @Override
    public String next(String t) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void print(String t) {
        // TODO Auto-generated method stub
        
    }

}

// 含有泛型方法的接口及其子类
public interface Generator<T> {
    T next(T t);
    <U> void print(U t);
}

public class SubGenerator implements Generator<String>{

    @Override
    public String next(String t) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <U> void print(U t) {
        // TODO Auto-generated method stub
        
    }

}

Demo 2

即静态方法无法访问泛型类上定义的泛型,此时采用泛型方法可解决。

// 正确做法
public class StaticGenerator {
    public static <T> T rebey(T t) {
        return null;
    }
}

Demo 3

在《深入理解Java虚拟机 JVM高级特性与最佳实践(高清完整版)》P271 介绍了“当泛型遇见重载”的例子。得出的结论是:

两个方法如果有相同的名称和特征签名,但返回值不同,那他们也是可以合法地共存于一个Class文件中的。

public String url(String s) {
    return "rebey.cn";
}

public int url(String s) {
    return 0;
}

然而以上代码是无法编译通过的。因为此书出版时还未发布JDK7,因此其实只有JDK1.6及以下才能编译通过。随着时间的推移,原来错的可能所以还是按我们原来的理解来解读重载就好了。同名方法参数个数、顺序、类型不同,与返回值类型无关。

Demo 4

public class Utilities {
    public static <T> HashSet<T> create(int size) {
        return new HashSet<T>(size);
    }
     
    public static void print( HashSet<String> h) { 
        for (String s : h) System.out.println(s);
    }
}

public class ResultGerneric {
    public static void main(String[] args) {
        Utilities.print(Utilities.create(10));    // error in Java 5,6,7 ; fine in Java 8
    }
}

这是来自GenericsFAQ403中的一个例子。笔者在java7上测试了,如愿的看到了错误提示:“The method print(HashSet<String>) in the type Utilities is not applicable for the arguments (HashSet<Object>)”。由于Utilities.create方法未指定具体类型,默认转为Object,所以Utilities.print此时无法接收其作为String类型的参数。而在Java8版本的增加了类型推断(type argument inference),能够根据赋值符号左边值类型自动推断出右边。

此外,通过显示指定参数类型也能够解决上述问题:

public class ResultGerneric {
    public static void main(String[] args) {
        Utilities.print(Utilities.<String>create(10));//点操作符与方法名之间
    }
}

在《Think In Java》4th中泛型章节也提到了相似的例子(P363)。

说点什么

通过几个泛型的例子,我们看到:随着时间的推移,对的可能错,错的亦能对。

这不是很有趣的一件事吗?

待续...


更多有意思的内容,欢迎访问笔者小站: rebey.cn


花田土著
190 声望34 粉丝

希望加入有意思的开发团队!