回答:
問題は、リテラルnullを使用すると、Javaはそれがどのタイプであると想定されているのかを認識できないことです。nullオブジェクト、またはnullオブジェクト配列の場合があります。単一の引数については、後者を想定しています。
2つの選択肢があります。nullを明示的にObjectにキャストするか、厳密に型指定された変数を使用してメソッドを呼び出します。以下の例をご覧ください。
public class Temp{
public static void main(String[] args){
foo("a", "b", "c");
foo(null, null);
foo((Object)null);
Object bar = null;
foo(bar);
}
private static void foo(Object...args) {
System.out.println("foo called, args: " + asList(args));
}
}
出力:
foo called, args: [a, b, c]
foo called, args: [null, null]
foo called, args: [null]
foo called, args: [null]
asList()
はjava.util.Arrays
クラスからの静的インポートです。私はそれが明白であると思いました。今はそれについて考えていArrays.toString()
ますが、リストに変換される唯一の理由はきれいに印刷されるためです。
への明示的なキャストが必要ですObject
:
foo((Object) null);
それ以外の場合、引数は可変引数が表す配列全体であると見なされます。
これを説明するテストケース:
可変引数を取るメソッド宣言を含むJavaコード(たまたま静的です):
public class JavaReceiver {
public static String receive(String... x) {
String res = ((x == null) ? "null" : ("an array of size " + x.length));
return "received 'x' is " + res;
}
}
このJavaコード(JUnit4テストケース)は上記を呼び出します(テストケースを使用して何もテストせず、出力を生成するだけです)。
import org.junit.Test;
public class JavaSender {
@Test
public void sendNothing() {
System.out.println("sendNothing(): " + JavaReceiver.receive());
}
@Test
public void sendNullWithNoCast() {
System.out.println("sendNullWithNoCast(): " + JavaReceiver.receive(null));
}
@Test
public void sendNullWithCastToString() {
System.out.println("sendNullWithCastToString(): " + JavaReceiver.receive((String)null));
}
@Test
public void sendNullWithCastToArray() {
System.out.println("sendNullWithCastToArray(): " + JavaReceiver.receive((String[])null));
}
@Test
public void sendOneValue() {
System.out.println("sendOneValue(): " + JavaReceiver.receive("a"));
}
@Test
public void sendThreeValues() {
System.out.println("sendThreeValues(): " + JavaReceiver.receive("a", "b", "c"));
}
@Test
public void sendArray() {
System.out.println("sendArray(): " + JavaReceiver.receive(new String[]{"a", "b", "c"}));
}
}
これをJUnitテストとして実行すると、次の結果が得られます。
sendNothing():受信した 'x'はサイズ0の配列です sendNullWithNoCast():受信した 'x'がnullです sendNullWithCastToString():受信した 'x'はサイズ1の配列です sendNullWithCastToArray():受信した 'x'がnullです sendOneValue():受信した 'x'はサイズ1の配列です sendThreeValues():受信した 'x'はサイズ3の配列です sendArray():受信した 'x'はサイズ3の配列です
これをもっと面白くするためにreceive()
、Groovy 2.1.2から関数を呼び出して、何が起こるか見てみましょう。結果は同じではないことがわかりました!これはバグかもしれません。
import org.junit.Test
class GroovySender {
@Test
void sendNothing() {
System.out << "sendNothing(): " << JavaReceiver.receive() << "\n"
}
@Test
void sendNullWithNoCast() {
System.out << "sendNullWithNoCast(): " << JavaReceiver.receive(null) << "\n"
}
@Test
void sendNullWithCastToString() {
System.out << "sendNullWithCastToString(): " << JavaReceiver.receive((String)null) << "\n"
}
@Test
void sendNullWithCastToArray() {
System.out << "sendNullWithCastToArray(): " << JavaReceiver.receive((String[])null) << "\n"
}
@Test
void sendOneValue() {
System.out << "sendOneValue(): " + JavaReceiver.receive("a") << "\n"
}
@Test
void sendThreeValues() {
System.out << "sendThreeValues(): " + JavaReceiver.receive("a", "b", "c") << "\n"
}
@Test
void sendArray() {
System.out << "sendArray(): " + JavaReceiver.receive( ["a", "b", "c"] as String[] ) << "\n"
}
}
これをJUnitテストとして実行すると、次の結果が得られます。Javaとの違いは太字で強調表示されています。
sendNothing():受信した 'x'はサイズ0の配列です sendNullWithNoCast():受信した 'x'がnullです sendNullWithCastToString():受信した 'x'がnullです sendNullWithCastToArray():受信した 'x'がnullです sendOneValue():受信した 'x'はサイズ1の配列です sendThreeValues():受信した 'x'はサイズ3の配列です sendArray():受信した 'x'はサイズ3の配列です
私は好む
foo(new Object[0]);
Nullポインタ例外を回避するため。
それが役に立てば幸い。
foo()
ですか?
メソッドのオーバーロード解決の順序は(https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2)です。
最初のフェーズでは、ボックス化またはボックス化解除の変換、または変数アリティメソッドの呼び出しを使用せずに、過負荷の解決を実行します。このフェーズ中に該当するメソッドが見つからない場合、処理は第2フェーズに進みます。
これにより、Java SE 5.0より前のJavaプログラミング言語で有効だった呼び出しは、変数アリティメソッドの導入、暗黙的なボックス化やボックス化解除の結果として、あいまいと見なされないことが保証されます。ただし、可変アリティメソッド(8.4.1)の宣言により、特定のメソッドメソッド呼び出し式に選択されたメソッドが変更される可能性があります。これは、可変アリティメソッドが最初のフェーズで固定アリティメソッドとして扱われるためです。たとえば、m(Object)を既に宣言しているクラスでm(Object ...)を宣言すると、m(Object []のように、一部の呼び出し式(m(null)など)でm(Object)が選択されなくなります。 )はより具体的です。
2番目のフェーズでは、ボックス化とボックス化解除を許可しながら過負荷の解決を実行しますが、可変アリティメソッドの呼び出しは使用できません。このフェーズ中に該当するメソッドが見つからない場合、処理は3番目のフェーズに進みます。
これにより、固定アリティメソッドの呼び出しで適用可能な場合、可変アリティメソッドの呼び出しでメソッドが選択されることがなくなります。
3番目のフェーズでは、オーバーロードを可変アリティメソッド、ボックス化、ボックス化解除と組み合わせることができます。
foo(null)
最初のフェーズfoo(Object... arg)
と一致しarg = null
ます。 arg[0] = null
決して起こらない第三段階です。
asList
サンプルにメソッドとその目的を記載していれば、他の人に役立つでしょう。