2013-06-04 14 views
15

Javaでgetterメソッドを使用する方法について質問があります。私はこのクラスを持っていたと仮定し :getterの結果を変更するとオブジェクト自体に影響がありますか?

class Test { 
    private ArrayList<String> array = new ArrayList<String>(); 

    public ArrayList getArray() { 
     return this.array; 
    } 

    public void initArray() { 
     array.add("Test 1"); 
     array.add("Test 2"); 
    } 
} 

class Start { 
    public static void main(String args[]) { 
     initArray(); 
     getArray().remove(0); 
    } 
} 

私の質問は次のとおりです。

は、実際のArrayListオブジェクトは修正されるだろう( "テスト1"、それから削除)?私は場所でこれを見たと思うが、ゲッターは単にそのオブジェクトのコピーを提供していると思った。それへの参照ではありません。それは(基準として)そのように動作した場合、この作業は、同様に(クラスTestのArrayListの目的は、同様に、このことによって変更されることになる)だろう?:

class Start { 
    public static void main(String args[]) { 
     initArray(); 
     ArrayList aVar = getArray(); 
     aVar.remove(0); 
    } 
} 
+3

Javaはオブジェクトをコピーしません。 – SLaks

+0

この例では、ゲッターはオブジェクトをどこで変更しますか? – raina77ow

+0

確認しやすいようです。実際には、変更が元の配列に影響するように、参照のみがあなたの例で渡されます。 – assylias

答えて

21

Javaは、配列への参照を返しますそれはコピーではなく、Listを変更します。一般的に、プリミティブ型(intfloatなど)でない限り、オブジェクトへの参照を取得します。

重複が返されるようにするには、自分で配列を明示的にコピーする必要があります。

+2

「これらのタイプのラッパー」の場合でも参考になります... –

+0

+1一般的で簡潔な答えです。 – MegaWidget

+0

答えをありがとう。したがって、コードの2番目のセクションでも、元のオブジェクトも同様に修正されます。 – Jordanss10

2

私がそれを理解する方法では、オブジェクト参照変数は、オブジェクト自体のメモリアドレスよりもわずかです。したがって、getArray()から返されるのは、そのArrayListへの参照変数です。オブジェクトには多くの参照変数があるかもしれませんが、変更されるのは同じオブジェクトです。

Javaはすべて値渡しです。したがって、オブジェクト参照変数をパラメータとして渡すか値を返すときはいつでも、オブジェクト参照変数の値を渡したり返すことになります。

0

あなたの質問に答えるには、ゲッターで変数に直接アクセスできます。 このコードを実行すると、ArrayListのStringが削除されていることがわかります。しかし、この例のように静的なArraListをコード内で使用しないでください。

public class Test { 

     private static ArrayList<String> array = new ArrayList<String>(); 

     public static ArrayList<String> getArray() { 
      return array; 
     } 

     public static void initArray() { 
      array.add("Test 1"); 
      array.add("Test 2"); 
     } 

    public static void main(String[] args) { 
      initArray(); 
      ArrayList aVar = getArray(); 
      aVar.remove(0); 
      System.out.println(aVar.size()); 
    } 

} 
0

としては、あなたのゲッターはリストを変更しない、それがリストに修正参照を返し、指摘しました。 FindBugsのようなツールは、あなたがそれと一緒に暮らすとあなたのリストを壊していないためにあなたのクラスのユーザーを信頼し、またはあなたのリストを変更できないの参照を返すために、これを使用することのいずれか...それについて警告を表示します:

public static List<String> getArray() { 
      return Collections.unmodifiableList(array); 
     } 
+0

この場合、これは参照渡しまたは値渡しとみなされますか?そして、 "getterがリストを変更するのではなく"リストに変更可能な参照を返す "ということは、getArray()の参照に加えられた変更が、配列内のarraylistオブジェクトに影響することを意味します。テストクラス? – Jordanss10

0

ことゲッターはあなたが呼んでいるオブジェクトを修正しません。純粋に慣習の問題です。確かにターゲットのIDは変更されませんが、内部状態が変更される可能性があります。ここで有用な例だ、少し大ざっぱ場合:

public class Fibonacci { 
    private static ConcurrentMap<Integer, BigInteger> cache = 
     new ConcurrentHashMap<>(); 

    public BigInteger fibonacci(int i) { 
     if (cache.containsKey(i)) { 
      return cache.get(i); 
     } else { 
      BigInteger fib = compute(i); // not included here. 
      cache.putIfAbsent(i, fib); 
      return fib; 
    } 
} 

ので、Fibonacci.fibonacci(1000)を呼び出すと、対象の内部状態を変更するが、それはまだ同じターゲットだことがあります。

は今、ここで可能なセキュリティ違反です:

public class DateRange { 
    private Date start; 
    private Date end; 
    public DateRange(final Date start, final Date end) { 
     if (start.after(end)) { 
      throw new IllegalArgumentException("Range out of order"); 
     } 
     this.start = start; 
     this.end = end; 
    } 
    public Date getStart() { 
     return start; 
    } 
    // similar for setStart, getEnd, setEnd. 
} 

問題はjava.lang.Dateが可変であるということです。誰かが次のようなコードを書くことができます:

DateRange range = new DateRange(today, tomorrow); 
// In another routine. 
Date start = range.getStart(); 
start.setYear(2088); // Deprecated, I know. So? 

現在の範囲はありません。それは、レジ係にあなたの財布を渡すようなものです。

これは、これらのうちの1つを実行するのが最善の理由です。これより前の方が優先されるためです。

  1. 可能な限り多くのオブジェクトを変更できません。これがJoda-Timeが書かれた理由とJava 8で日付が再び変更される理由です。
  2. 守備的なコピーを1セット取得します。
  3. アイテムの不変ラッパーを返します。
  4. コレクションをiterableとして返します。もちろん、誰かがそれを戻すかもしれません。
  5. アイテムにアクセスするためのプロキシを返します。そのタイプにキャストできません。

私は知っています。 CまたはC++が必要な場合は、どこでそれらを見つけるか知っています。 1.返信

1

他にも、プリミティブ型でない限り、オブジェクトへの参照があります。これはC++のポインタと似ていますが、オブジェクトにアクセスすることはできますが、C++リファレンス(変数のメモリアドレスへのポインタ)とは異なり、別のオブジェクトと置き換えることはできません。セッターだけがそれを行うことができます。

質問には2種類あります。test.getArray().remove(0)aVar.remove(0)です。それらの結果には違いはありませんが、それはまだポインタのような参照であり、元のものを変更します。

getterを呼び出すだけでクローンを取得することはないので、オブジェクトが不変でない限り、ゲッターがアクセス権を与えたオブジェクトを変更できます。たとえば、Stringは不変ですが、任意の基本CollectionArrayListを含む)は変更可能です。 Collections.unmodifiable*(...)に電話してコレクションを変更できないようにすることができます。ただし、コレクションの項目が変更可能な場合でも変更することができます。

場合によっては、クローンを取得することは良い考えです。ほとんどの場合、そうではありません。 getterは何も複製してはいけません。nullの可能性のあるコレクションなどを初期化しない限り、データを変更すべきではありません。不変オブジェクトを含む変更不可能なコレクションが必要な場合は、このようにしてください。この例では、後で説明する理由Fooを実装するクラスFooImplがあります。

public interface Foo { 
    int getBar(); 
} 

public class FooImpl Foo { 
    private int bar; 
    @Override 
    public int getBar() { 
     return bar; 
    } 
    public void setBar(int newValue) { 
     this.bar = newValue; 
    } 
} 

ご覧のとおり、Fooにはセッターがありません。 ArrayList<Foo>を作成してゲッターからCollections.unmodifiableList(myArrayList)として渡すと、ほとんどあなたがやったようです。しかし、仕事はまだ行われていません。クラスFooImplが公開されている場合(この場合)、リスト内に見つかったfooinstanceof FooImplである場合、誰かが試してみる可能性があります。それを(FooImpl) fooとして変更可能にしてください。ただし、FooFooWrapperというラッパーにラップできます。それだけでなくFooを実装します。

public class FooWrapper implements Foo { 
    private Foo foo; 
    public FooWrapper(Foo foo) { 
     this.foo = foo; 
    } 
    public int getBar() { 
     return foo.getBar(); 
    } 
    // No setter included. 
} 

その後、我々はCollection<FooWrapper>new FooWrapper(myFoo)を置くことができます。このラッパーにはパブリックセッターはなく、内部のfooはプライベートです。基礎となるデータは変更できません。今度はFooインターフェイスについて。 FooImplFooWrapperの両方が実装されていますが、いずれの方法でもデータを変更しようとしない場合、入力時にFooを入力することができます。どのFooを取得するかは関係ありません。

したがって、変更不可能なデータを含む変更不可能なコレクションを作成する場合は、オブジェクトをフィードし、Collections.unmodifiable*(theCollection)を呼び出して新しいCollection<Foo>を作成します。または、例えば、FooWrappersを返し、このリストはFooのコレクション全体をラップカスタムコレクションます包まれたコレクションで

public MyUnmodifiableArrayList implements List<Foo> { 
    ArrayList<Foo> innerList; 
    public get(int index) { 
     Foo result = innerList.get(index); 
     if (!(result instanceof FooWrapper)) { 
      return new FooWrapper(result); 
     } 
     return result; // already wrapped 
    } 
    // ... some more List interface's methods to be implemented 
} 

を、あなたは、元のコレクションを反復処理とのラッパーとそのクローンを作成する必要はありませんデータ。この解決策は、全体を読むのがはるかに優れていますが、そのインデックスのFooがすでにFooWrapperでない限り、get()を呼び出すたびに新しいFooWrapperが作成されます。 get()への何百万回の呼び出しで長時間実行されているスレッドでは、これはガベージコレクタの不要なベンチマークになる可能性があり、既存のFooWrappersを含む内部配列またはマップを使用できます。

新しいカスタムList<Foo>を返品することができます。しかし、再び、普通のゲッターからではありません。 private ArrayList<FooImpl> fooListフィールドにはgetUnmodifiableFooList()のようになります。

関連する問題