3

我正在阅读 Joshua Bloch 的 Effective Java 2nd edition,第 25 项(第 122 页)。当您进一步阅读本章时,您会到达作者编写以下代码的地步:

// Naive generic version of reduction - won't compile!
static <E> E reduce(List<E> list, Function<E> f, E initVal) {
    E[] snapshot = list.toArray(); // Locks list
    E result = initVal;
    for (E e : snapshot)
    {
        result = f.apply(result, e);
    }
    return result;
}

然后作者声明编译器不会编译它,因为您需要在分配的行中添加显式强制转换E[] snapshot = list.toArray();,从而导致此E[] snapshot = (E[]) list.toArray();,然后您会收到一条警告说有[unchecked] unchecked cast


Q1:我知道这本书考虑到了 Java 6 的变化(我们现在几乎是 Java 8)。但是,我编写了相同的方法,从编译中得到相同的错误。这是因为我需要添加显式演员表。但是没有警告。那么这个警告是关于什么的呢?


Q2:作者声明以下方法可行,但事实证明它不是类型安全的。

稍加修改,您就可以让它 ClassCastException在不包含显式转换的行上抛出 a 。

好的,我明白了......但是我怎样才能让它抛出一个ClassCastException


如果您想自己检查一下,我会在这篇文章中留下一个准备运行的示例:

import java.util.Arrays;
import java.util.List;

public class Main
{

    public static void main(String[] args)
    {
        List<Integer> ints = Arrays.asList(10,20,30);
        Integer result = reduce (ints,new Function<Integer>() {

            @Override
            public Integer apply(Integer arg1, Integer arg2)
            {
                return arg1 + arg2;
            }
        },0);
        System.out.println(result);
    }

    static <E> E reduce(List<E> list, Function<E> f, E initVal)
    {
        E[] snapshot = (E[]) list.toArray(); // Locks list
        E result = initVal;
        for (E e : snapshot)
        {
            result = f.apply(result, e);
        }
        return result;
    }

    interface Function<T>
    {
        public T apply(T arg1, T arg2);
    }
}
4

4 回答 4

1

问题一:为什么会E[] snapshot = (E[]) list.toArray();出现不安全检查的警告?

原因是,在 java 中,数组保留了它们的类型信息,而泛型则被删除了。在运行时所有List<String>List<Dog>并且List<NuclearBomb>都是有效的List<Object>

所以:

List<E> list变成List<Object>

E[] array = (E[]) list.toArray();

是不安全的。

问题二:在运行时怎么会失败?

如果java让你做

E[] array = (E[]) list.toArray();

使用Objects 列表,它还可以让您执行以下操作:

String[] array = (String[]) list.toArray();

因为字符串毕竟是对象。该行将编译(带有警告)并且在运行时你会得到一个强制转换异常。

于 2014-05-17T16:37:35.337 回答
1
E[] snapshot = list.toArray();

list.toArray()返回Object[]而不是E[]. 所以你需要一个明确的演员。但是像这样的显式(或动态)强制转换的存在意味着编译器无法保证那里的类型安全。相反,您是在告诉编译器一切都很好,实际上它会E[]在运行时出现。所以,它会给你一个警告,说 - unchecked cast。请注意,如果编译器能够保证那里的类型安全,那么它将是一个静态/检查转换(由编译器本身隐式执行),而不是。

于 2014-05-17T16:06:01.257 回答
1

我认为编译器在看到显式转换时会在泛型和数组组合时发出警告。如果您还记得 Bloch 有效 Java 第 117 页中的较早示例

return (T[]) Arrays.copyOf(elements, size, a.getClass());

在上述场景中,编译器也会生成警告:[unchecked] unchecked cast。尽管我们在运行时非常确定返回的数组始终是 T 类型。但是通过使用 @SuppressWarnings 注释来抑制它。

于 2015-02-14T09:09:52.767 回答
0

对于问题2,我尝试通过下面的代码来理解作者。

呼叫者:

List<String> list = Arrays.asList("string");
reduce(list, new Function<String>() {
    @Override
    public String apply(String arg1, String arg2) {
        return "";
    }
}, "");

“减少”方法:

static <E> E reduce(List<E> list, Function<E> f, E initVal) {
    // /*
    List<E> snapshot;
    synchronized (list) {
        snapshot = new ArrayList<>(list);
    }
    snapshot.set(0, 123);    // compile error here
    // */

    /*Object[] objs = list.toArray();
    objs[0] = 123;  // will cause ClassCastException on runtime
    E[] snapshot = (E[]) objs;*/
    E result = initVal;
    for (E e : snapshot) {
        result = f.apply(result, e);
    }
    return result;
}

也许它不是这样的“稍作修改”,但它是一个适当的解释。

于 2016-01-19T10:45:03.177 回答