Java核心技术第12章(4)

12.8    通配符类型

    通配符类型Pair<? extends Employee>表示任何泛型Pair类型,它的类型参数是Employee的子类,如Pair<Manager>,但不是Pair<String>.
    假设要编写一个打印雇员对的方法,像这样:
public static void printBuddies(Pair<Employee> p)
{
    Employee first = p.getFirst();
    Employee second = p.getSecond();
    System.out.println(first.getName() + " and " + second.getName() + " are buddies.";
}
    正如前面讲到的,不能将Pair<Manager>传递给这个方法.这一点很受限制,解决的方法很简单:使用通配符类型:
public static void printBuddies(Pair<? extends Employee> p)
    类型Pair<Manager>是Pair<? extends Employee>的子类型.
    使用通配符会通过Pair<? extends Employee>的引用破坏Pair<Manager>吗?
Pair<Manager> managerBuddies = new Pair<>(ceo, cfo);
Pair<? extends Employee> wildcarcBuddies = managerBuddies;      // ok
wildcardBuddies.setFirst(lowlyEmployee);
    这可能不会引起破坏.仔细看一看类型Pair<? extends Employee> 其方法似乎是这样的:
? extends Employee getFirst()
void setFirst(? extends Employee)
    这样将 不可能调用setFirst方法.编译器只知道需要某个Employee的子类型,但不知道具体是什么类型.它拒绝传递任何特定的类型,毕竟?不能用来匹配.
    使用getFirst就不存在这个问题:将getFirst的返回值赋给一个Employee的引用完全合法.
    这就是引入有限定的通配符的关键之处
,现在已经有办法区分安全的访问器和不安全的更改器的方法了.

12.8.1  通配符的超类型限定

    通配符限定与类型变量限定十分类似,但是,还有一个附加的能力,即 可以指定一个超类型限定(supertype bound),如下所示:
? super Manager
    这个通配符限制为Manager的所有超类型.
    为什么要这样做呢?带有超类型限定的通配符的行为与12.8节介绍的相反,可以为方法提供参数,但不能使用返回值.例如,Pair<? super Manager>有方法
void setFirst(? super Manager)
? super Manager getFirst()
    编译器不知道setFirst方法的确切类型,但是可以用任意Manager对象调用它,而不是能用Employee对象调用.然而,如果调用getFirst,返回的对象类型就不会得到保证.只能把它赋给一个Object.
    直观地讲, 带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取.
    下面是超类型限定的另一种应用.Comparable接口本身就是一个泛型类型.声明如下:
public interface Comparable<T>
{
    public int compareTo(T other);
}
    在此,类型变量指示了other参数的类型.
    由于Comparable是一个泛型类型,也许可以把ArrayAlg类的min方法做的更好一些,可以这样声明:
public static <T extends Comparable<T>> T min(T[] a)
    看起来,这样写比只使用T extends Comparable更彻底,并且对许多类来讲,工作得更好.

12.8.2  无限定通配符

    还 可以使用无限定的通配符,例如,Pair<?>.初看起来,这好像与原始的Pair类型一样.实际上,有很大不同,类型Pair<?>有方法如下所示:
? getFirst()
void setFirst(?)
    getFirst的返回值只能赋给一个Object,setFirst方法不能被调用,甚至不能用Object调用. Pair<?>和Pair本质的不同在于:可以用任意Object对象调用原始的Pair类的setObject方法.
    注释:可以调用setFirst(null).
    为什么要使用这样脆弱的类型?它对于许多简单的操作非常有用.例如,下面这个方法将用来测试一个Pair是否包含一个 null 引用,它不需要实际的类型
public static boolean hasNulls(Pair<?> p)
{
    return p.getFirst() == null || p.getSecond() == null;
}
    通过将hasNulls转换成泛型方法,可以避免使用通配符类型:
public static <T> boolean hasNulls(Pair<T> p)
    但是,带有通配符的版本可读性更强.

12.8.3  通配符捕获

    编写一个交换一个Pair元素的方法:
public static void swap(Pair<?> p)
    通配符不是类型变量,因此,不能在编写代码中使用"?"作为一种类型.也就是说,下述代码是非法的:
? t = p.getFirst();     // error
p.setFirst(p.getSecond());
p.setSecond(t);
    这是一个问题,因为在交换的时候必须临时保存第一个元素.幸运的是,这个问题有一个有趣的 解决方法,可以写一个辅助方法法swapHelper,如下所示:
public static <T> void swapHelper(Pair<T> p)
{
    T t = p.getFirst();
    p.setFirst(p.getSecond());
    p.setSecond(t);
}
    注意,swapHelper是一个泛型方法,而swap不是,它具有固定的Pair<?>类型的参数.
    现在可以由swap调用swapHelper:
public static void swap(Pair<?> p) { swapHelper(p); }
    在这种情况下,swapHelper方法的参数T捕获通配符.它不知道是哪种类型的通配符,但是,这是一个明确的类型,并且<T>swapHelper的定义只有在T指出类型时才有明确的含义.
    pair3/PairTest3.java如下所示:
    package pair3;

public class PairTest3
{
    public static void main(String[] args)
    {
        Manager ceo = new Manager("Gus Greedy", 80000, 2003, 12, 15);
        Manager cfo = new Manager("Sid Sneaky", 60000, 2003, 12, 11);
        Pair<Manager> buddies = new Pair<>(ceo, cfo);
        printBuddies(buddies);

        ceo.setBonus(100000);
        cfo.setBonus(500000);
        Manager[] managers = {ceo, cfo};
        Pair<Employee> result = new Pair<>();
        minmaxBonus(managers, result);
        System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
        maxminBonus(managers, result);
        System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());

    }

    public static void printBuddies(Pair<? extends Employee> p)
    {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
    }

    public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
    {
        if (a == null || a.length == 0)
            return ;
        Manager min = a[0];
        Manager max = a[0];
        for (int i = 1; i < a.length; i++)
        {
            if (min.getBonus() > a[i].getBonus())
                min = a[i];
            if (max.getBonus() < a[i].getBonus())
                max = a[i];
        }
        result.setFirst(min);
        result.setSecond(max);
    }
    
    public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
    {
        minmaxBonus(a, result);
        PairAlg.swapHelper(result);
    }
}

class PairAlg
{
    public static boolean hasNulls(Pair<?> p)
    {
        return p.getFirst() == null || p.getSecond() == null;
    }
    public static void swap(Pair<?> p) { swapHelper(p); }
    public static <T> void swapHelper(Pair<T> p)
    {
        T t = p.getFirst();
        p.setFirst(p.getSecond());
        p.setSecond(t);
    }
}
    运行结果如下所示:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值