Java谜题7:饼干-解决方案


乍一看,cookiemonster.eat需要返回一个负长度的String。仔细看,有两个单独的要求:第一次调用eat时,它需要返回一个空String;第二次,它需要返回一个负值length()。

诀窍是,泛型方法的返回类型可能会有所不同,具体取决于使用它的类型上下文。为了证明这一点,让我们来看一个示例java.util.Collections:<T> List <T> emptyList()。在方法签名中,<t>表示方法上有类型变量t,list<t>是返回类型。您可以将其用作List<String> strings = Collections.emptyList();。这是因为类型推断确保返回类型(list<t>)中的t调整为它被分配给的变量类型(list<string>)。如果没有上下文(未分配任何内容),则推断的类型将是 Object(T的隐式上界)。在Collections.emptyList().size()中,例如emptyList()返回一个List<Object>。

回到我们的CookieMonster,我们可以让eat返回上下文期望它返回的任何内容:static<c>ceat(string cookie)。就像一块神奇的饼干。如果你认为是“巧克力”并把它放进嘴里,那它就是巧克力饼干。在count中,它被分配给一个String,它会变成一个Stringcookie(我不想知道那是什么味道)。这是一个开始,但是在cookiemonster.eat(nocookie).length()中,没有上下文,所以它变成了没有length()方法的Object ,并且无法编译。

我们通过在C上设置一个bound来修复这个问题。要编译两个eat调用,bound必须是String的超类,并且有一个length()方法。有一种类型:charsequence。因此,我们将类型变量<c>替换为<c extends charsequence>。在第一次调用中,eat(“cookie”)必须返回空值String,在第二次调用中,eat(“”)必须返回长度为负的charsequence:

package monster;
 
public class CookieMonster {
    public static <C extends CharSequence> C eat(String cookie) {
        return (C) (cookie.length() > 0 ? "" : new CharSequence() {
            public int length() {
                return -1;
            }
 
            public char charAt(int index) {
                throw new UnsupportedOperationException();
            }
 
            public CharSequence subSequence(int start, int end) {
                throw new UnsupportedOperationException();
            }
        });
    }
}