为什么在Java中无法在空指针上调用方法?

huangapple go评论82阅读模式
英文:

Why can't you call a method on a null pointer in Java?

问题

在Go语言中,只要不对空指针进行解引用,就可以调用空指针上的方法:

type empty struct{}
func (e *empty) Allocated() bool { return e != nil }

(要运行的代码,请点击这里

然而,在Java中,即使方法不解引用任何成员变量,调用空指针上的方法仍然会导致空指针异常:

class Test {
    public boolean Allocated() { return this != null; }
}

有人知道为什么会出现这种行为吗?它有什么优势吗?有什么想法吗?

英文:

In Go, it is OK to call a method on a null pointer so long as that pointer is never dereferenced:

<!-- language: lang-go -->

type empty struct{}
func (e *empty) Allocated() bool { return e != nil }

(For runnable code, click here)

In Java, however, calling a method on a null pointer, even if the method never dereferences any member variables, still causes a null pointer exception:

<!-- language: lang-java -->

class Test {
    public boolean Allocated() { return this != null; }
}

Does anybody know why this behavior exists? Is there some advantage that it gives? Thoughts?

答案1

得分: 12

这是因为所有的Java方法都是虚拟的。

当你写someInstance.Allocated()时,运行时需要检查someInstance是否实际上是一个派生类型,该类型重写了这个方法。

理论上,对于finalprivate方法,这个限制可以放宽。
我猜测语言设计者选择不这样做是为了保持一致性。(并且这样移除final不会造成破坏性变化)

英文:

This is because all Java methods are virtual.

When you write someInstance.Allocated(), the runtime needs to check whether someInstance is actually of a derived type that overrides the method.

In theory, this restriction could have been relaxed for final or private methods.
I assume that the language designers chose not to for consistency. (and so that removing final wouldn't be a breaking change)

答案2

得分: 5

_SLaks_的答案从Java的角度来看是很好的。我对Java一无所知,但我了解Go,这是我的答案:

首先,nil不同于NULL指针,它们之间有一些根本的区别

然后,Go中的方法不是类型的实例的一部分,而是类型本身,即Go不像Java那样在对象内部存储vtable:

var e *empty
fmt.Println(e.Allocated())

与以下代码等效(语法糖):

var e *empty
fmt.Println((*empty).Allocated(e)) // 这是有效的代码

注意Allocated作为*empty的成员被调用,就像传统的“面向对象”语言中的静态方法一样。

实际上,(*empty).Allocated只是一个带有奇怪符号的函数名,包括一个点、一个星号和括号。

因为接收器只是另一个参数,它是nil对于方法分派机制来说是无关紧要的。

> 在Go中,只要指针没有被解引用,就可以在空指针上调用方法

如果你所说的“可以”是合法的意思,那么甚至可以在nil值上调用方法并对其进行解引用。编译器不会报错,但你会得到一个运行时恐慌。

英文:

The answer by SLaks is good from a Java perspective. I don't have a clue about Java but I know Go and here's my answer:

First of all nil is not the same as a NULL pointer, there are some fundamental differences.

Then, methods in Go are not part of a type's instance but the type itself i.e. Go does not store the vtable inside objects, like Java does:

var e *empty
fmt.Println(e.Allocated())

is the same as&hellip; (syntactic sugar for&hellip;):

var e *empty
fmt.Println((*empty).Allocated(e)) // This is valid code

Note how Allocated is invoked as a member of *empty, much like a static method in traditional "OOP" languages.

In fact, (*empty).Allocated is just a function name with a weird notation that includes a dot, an asterisk and parens.

Because the receiver is just another argument, the fact that it is nil is unimportant to the method dispatch mechanism.

> In Go, it is OK to call a method on a null pointer so long as that pointer is never dereferenced

If by OK you mean legal, then it's even OK to call a method on a nil value and dereferencing it. The compiler won't complain - you'll just get a runtime panic.

答案3

得分: -2

我认为这是一个哲学问题;方法(除了静态方法)本质上是应用于对象的,因此在这种范式中,对一个非对象(如“null”)调用这样的方法是没有意义的。如果你想定义一个既可以应用于对象又可以应用于“null”的方法,很容易定义一个静态方法来实现,我认为这样做会使调用该方法的代码对读者来说更加清晰易懂。

英文:

I think it's a philosophical issue; methods (except for static methods) apply to an object, by their very nature, and calling a method like that on a non-object like "null" just doesn't make sense in this paradigm. If you want to define a method that can apply to an object or to "null", it's easy enough to define a static method that does that, and in my opinion that would make the code that calls the method less confusing to readers.

huangapple
  • 本文由 发表于 2013年7月10日 03:45:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/17556837.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定