首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java for-each循环抛出NullPointException

Java for-each循环抛出NullPointException
EN

Stack Overflow用户
提问于 2013-04-28 11:56:16
回答 8查看 53.9K关注 0票数 24

下面的java代码段将产生一个NullPointException,因为变量列表是null,它被传递给for-each循环。

代码语言:javascript
复制
List<> arr = null;
for (Object o : arr) {
    System.out.println("ln "+o);
}

我认为for (Object o : arr){ }等同于

for (int i = 0; i < arr.length; i++) { }

和/或

代码语言:javascript
复制
for (Iterator<type> iter = arr.iterator(); iter.hasNext(); ){ 
   type var = iter.next(); 
}

在任何一种情况下,arr为null都会导致arr.length或arr.iterator()抛出NullPointException

我只是好奇为什么for (Object o : arr){ }没有被翻译成

代码语言:javascript
复制
if (arr!=null){
  for (int i = 0; i < arr.length; i++) { 
  }
}
and
if (arr!=null){
    for (Iterator<type> iter = arr.iterator(); iter.hasNext(); ){ 
       type var = iter.next(); 
    }
}

包含arr!=null表达式可以减少代码嵌套。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2013-04-28 12:07:37

我看到了以下原因,尽管我不知道是否有人考虑过这一点,它是什么时候实现的,以及实际原因是什么。

正如您演示的那样,(:)-loop的当前行为非常容易理解。另一种行为不是for循环,它将是java世界中唯一这样的行为。for循环并不等同于简单的equivalent

  1. Using循环,因此在两者之间迁移实际上不是一个空的习惯无论如何都是一个坏习惯,所以NPE是一种很好的方式来告诉开发人员“你他妈的清理了你的烂摊子”,这个问题将被隐藏起来。
  2. 如果你想在循环之前或之后对数组做任何其他的事情怎么办……
  3. 现在,您的代码中将有两次null检查。
票数 21
EN

Stack Overflow用户

发布于 2013-04-28 13:38:45

回答你的第一个问题:不,这三个循环不是等价的。其次,在这些循环中找不到null检查;尝试迭代不存在的循环没有任何意义。

假设我们有以下类:

代码语言:javascript
复制
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class EnhancedFor {


    private List<Integer> dummyList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    private List<Integer> nullList = null;

    public void enhancedForDummyList() {
        for(Integer i : dummyList) {
            System.out.println(i);
        }
    }

    public void iteratorDummyList() {
        for(Iterator<Integer> iterator = dummyList.iterator(); iterator.hasNext();) {
            System.out.println(iterator.next());
        }
    }

    public void normalLoopDummyList() {
        for(int i = 0; i < dummyList.size(); i++) {
            System.out.println(dummyList.get(i));
        }
    }
}

我们将把它分解成字节码,看看这些循环之间是否有什么不同。

1:增强的For vs.

下面是增强的for循环的字节码。

代码语言:javascript
复制
public enhancedForDummyList()V
   L0
    LINENUMBER 12 L0
    ALOAD 0
    GETFIELD EnhancedFor.dummyList : Ljava/util/List;
    INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
    ASTORE 1
   L1
   FRAME APPEND [java/util/Iterator]
    ALOAD 1
    INVOKEINTERFACE java/util/Iterator.hasNext ()Z
    IFEQ L2
    ALOAD 1
    INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
    CHECKCAST java/lang/Integer
    ASTORE 2
   L3
    LINENUMBER 13 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L4
    LINENUMBER 14 L4
    GOTO L1
   L2
    LINENUMBER 15 L2
   FRAME CHOP 1
    RETURN
   L5
    LOCALVARIABLE i Ljava/lang/Integer; L3 L4 2
    LOCALVARIABLE i$ Ljava/util/Iterator; L1 L2 1
    LOCALVARIABLE this LEnhancedFor; L0 L5 0
    MAXSTACK = 2
    MAXLOCALS = 3

下面是迭代器的字节码。

代码语言:javascript
复制
public iteratorDummyList()V
   L0
    LINENUMBER 24 L0
    ALOAD 0
    GETFIELD EnhancedFor.dummyList : Ljava/util/List;
    INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
    ASTORE 1
   L1
   FRAME APPEND [java/util/Iterator]
    ALOAD 1
    INVOKEINTERFACE java/util/Iterator.hasNext ()Z
    IFEQ L2
   L3
    LINENUMBER 25 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
    GOTO L1
   L2
    LINENUMBER 27 L2
   FRAME CHOP 1
    RETURN
   L4
    LOCALVARIABLE iterator Ljava/util/Iterator; L1 L2 1
    // signature Ljava/util/Iterator<Ljava/lang/Integer;>;
    // declaration: java.util.Iterator<java.lang.Integer>
    LOCALVARIABLE this LEnhancedFor; L0 L4 0
    MAXSTACK = 2
    MAXLOCALS = 2

最终,它们看起来确实在做非常相似的事情,,它们使用相同的接口。不同之处在于,增强的for循环使用两个变量作为当前值(i),游标指向列表的其余部分(i$),而迭代器只需要游标来调用.next()

相似,但并不完全相同。

2.增强的For与for-Loop

让我们为for循环添加字节码。

代码语言:javascript
复制
public normalLoopDummyList()V
   L0
    LINENUMBER 24 L0
    ICONST_0
    ISTORE 1
   L1
   FRAME APPEND [I]
    ILOAD 1
    ALOAD 0
    GETFIELD EnhancedFor.dummyList : Ljava/util/List;
    INVOKEINTERFACE java/util/List.size ()I
    IF_ICMPGE L2
   L3
    LINENUMBER 25 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 0
    GETFIELD EnhancedFor.dummyList : Ljava/util/List;
    ILOAD 1
    INVOKEINTERFACE java/util/List.get (I)Ljava/lang/Object;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L4
    LINENUMBER 24 L4
    IINC 1 1
    GOTO L1
   L2
    LINENUMBER 27 L2
   FRAME CHOP 1
    RETURN
   L5
    LOCALVARIABLE i I L1 L2 1
    LOCALVARIABLE this LEnhancedFor; L0 L5 0
    MAXSTACK = 3
    MAXLOCALS = 2

,它正在做一些不同的事情。它根本没有使用 Iterator 接口,而是调用get(),它只由List指定,而不是Iterator

3.结论

有一个合理的理由来解释为什么我们要取消引用的列表被认为不为空--我们调用的是接口指定的方法。如果这些方法没有实现,那就不一样了:抛出一个UnsupportedOperationException。如果我们试图调用契约的对象不存在-那就没有意义了。

票数 11
EN

Stack Overflow用户

发布于 2016-03-14 20:02:59

循环null将导致NullPointerException,因此您必须始终检查列表是否为null,您可以使用以下泛型方法:

代码语言:javascript
复制
public static boolean canLoopList(List<?> list) {
    if (list != null && !list.isEmpty()) {
        return true;
    }
    return false;
}

然后,在循环任何列表之前,请检查列表:

代码语言:javascript
复制
if (canLoopList(yourList)) {
    for(Type var : yourList) {
    ...
}
}
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16259435

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档