2016-03-21 11 views
15

varargsを使用する場合、結果の配列のコンポーネントタイプはどのように決定されますか?varargs配列のコンポーネントタイプはどのように決定されますか?

たとえば、このプログラムはtrueを印刷することが保証されていますか、または技術的に不特定の動作ですか?

public static void main(String[] args) { 
    foo("", 0); 
} 

static <T> void foo(T... arr) { 
    System.out.println(arr.getClass() == Serializable[].class); 
} 
+2

ここでの答えは、「私が試したすべてのコンパイラには少なくともいくつかの不一致があります。 –

+1

おそらく、http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.5.1とそれに続く。 – assylias

答えて

1

まあ、私はこの約100%確実ではないが、私は、これは可変引数とは何の関係もないと思うが、より動的バインディング、ジェネリック医薬品と型消去などがあります。

"Javaの配列は共変ですが、ジェネリックはありません。つまり、String []はObject []のサブタイプですが、StackはStackのサブタイプではありません。

出典: http://algs4.cs.princeton.edu/13stacks/

だから、あなたの質問に答えるために、この動作は、文書に指定して期待されています。本の中で

:「効果的なJavaのジョシュア・ブロックはこのようにそれを説明する:

配列は、2つの重要な方法で、一般的なタイプとは異なります。第1に、アレイは共変である。この恐ろしい言葉は、単にSubがSuperのサブタイプである場合、配列型Sub []がSuper []のサブタイプであることを意味します。対照的に、ジェネリックスは不変である:Type1とType2の2つの異なるタイプの場合、リスト<タイプ1 >はリストのサブタイプでもスーパータイプでもない。<タイプ2 > [JLS、4.10; Naftalin07,2.5]。これは、ジェネリックが不完全であることを意味すると思うかもしれませんが、間違いなくそれは不十分な配列です。

なぜ私はあなたが聞くかもしれない配列について話していますか?なぜなら、varargsは最終的に配列になるからです。

"以前のリリースでは、任意の数の値を取得する方法では、配列を作成してメソッドを呼び出す前に値を配列に格納する必要がありました。

"複数の引数を配列に渡す必要がありますが、varargs機能はプロセスを自動化して非表示にすることは本当です。"

出典: https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html

2

この答えは、あなたが探している答えは100%ではないかもしれませんが、多分それは役立ちます。通常、arr.getClass()はいくつかの配列クラスを返します(最も一般的なのはObject[].classですが、Integer[].class,Number[].class、さらにはSerializable[].classでもあります。通常、すべての要素の最も特殊な型ですが、 szalik答え)。配列に含まれるすべてのクラスがSerializableのインスタンスであることを確認したい場合は、各要素をチェックする必要があります(btw。提示実装は)null値をサポートしていません:ところで

static <T> void foo(T... arr) { 
    System.out.println(Stream.of(arr) 
      .filter(e -> !Serializable.class.isInstance(e.getClass())) 
      .findFirst() 
      .orElse(null) == null); 
} 

は、あなたが見てしたいことがあります。

私はこれについて100%は確信していませんが、これはvarargsとは関係がありませんが、ダイナミックバインディング、ジェネリック、タイプ消去のようなものだと思います。

ちょうどあなたのfoo実装のいくつかの例:

  • foo(1, 2)Integer[].class
  • foo(1, 2.0)Number[].class
  • foo ("", 1)は次のようになりますでしょうだろうSerializable[].class
  • foo(null, 2)Integer[].class
  • foo("", new Object())だろうがそれはジェネリック医薬品について可変引数が、より多くの話ではありませんObject[].class
1

だろう。
コンパイル時に一般情報が失われます。 varargsパラメータは、コンパイラによって配列に変換されます。したがって、JVMは型を決定しませんが、コンパイラは型を決定します。

あなたのコードはfolowsとしてバイトコードにコンパイルされます。

public static void main(String[] args) { 
    foo(new Serializable[]{"", Integer.valueOf(0)}); 
} 

決定のアルゴリズムは非常に長いですが、あなたはここでそれについて読むことができます。 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

3

私はこのコードを実行し、出力はあなたが(クラスが異なる階層の枝に複数の共通の祖先を持っている、少なくとも場合)保証を持っていないよう指示

正直なところ、私はこの魔法の背後にある理由を知りません、しかし、私はちょうどコメント

import java.util.*; 
import java.lang.*; 
import java.io.*; 

class Ideone 
{ 

    interface A{} 
    interface B{} 
    class AB implements A, B {} 
    class BA implements A, B {} 

    public static void main (String[] args) throws java.lang.Exception 
    { 
     foo(new AB(), new BA()); 
     foo2(new AB(), new BA()); 
    } 

    static <T> void foo(T... arr) { 
     System.out.println(arr.getClass() == A[].class); 
    } 

    static <T> void foo2(T... arr) { 
     System.out.println(arr.getClass() == B[].class); 
    } 
} 

出力

true 
false 
として、それを投稿しませんでした

もっと奇妙なこと:

interface Binterface A前に宣言されている場合、結果は逆です:

false 
true 

引数の順序を変更するインタフェースのメソッド呼び出し、メソッドの宣言の順序と順序でimplementsブロックで行います私には効果がありません(1.8.0_51)。