山崎屋の技術メモ

IT業界で働く中で、気になること、メモしておきたいことを書いていきます。

Eclipse 関連記事のリンク集

Eclipse の設定まわりの記事など、思いついたときに書いていたら結構な記事数になった。一覧でまとまっていたほうが目的の記事を見つけやすいので、ここにまとめておく。

今後、Eclipse 関連の記事を書いたら、これも随時更新していく予定。

Java の引数、オブジェクトは「参照の値渡し」

オブジェクト編です。プリミティブ型(基本型)の記事はこちら

話はそれますが、プリミティブ型の反対は参照型というらしいのですが、
今回のテーマの中で「参照型」って言葉を使うと、混乱するので、「オブジェクト」にしています。

本題。
よく「Java ではすべて値渡し」って説明を初心者にしている人を見ます。間違いではないですが、ちょっと不親切な気がします。
丁寧に「参照の値渡し」と教えてあげましょう。

それでは、サンプルです。

メインクラスとは別に、メソッドの引数でやり取りするためのクラスを宣言しておきます。

public class Obj {
	public int a;
}

メインクラス

public class Argument {
	public static void main(String[] args) {
		Obj o = new Obj();
		o.a = 2;
		methodA(o);
		System.out.println("oが持つ変数aの内容は[" + o.a + "]");
	}

	private static void methodA(Obj o) {
		o.a = 3;
	}
}

実行結果

oが持つ変数aの内容は[3]

ただ、これだけだと「なんだ、参照渡しじゃん」と言われます。

なので、メインクラスをちょっと変えて、これも見せてやります。

public class Argument {
	public static void main(String[] args) {
		Obj o = new Obj();
		o.a = 2;
		methodA(o);
		System.out.println("oが持つ変数aの内容は[" + o.a + "]");
	}

	private static void methodA(Obj o) {
		o = new Obj();
		o.a = 3;
	}
}

実行結果

oが持つ変数aの内容は[2]

または、こう↓。

public class Argument {
	public static void main(String[] args) {
		Obj o = new Obj();
		o.a = 2;
		methodA(o);
		System.out.println("oが持つ変数aの内容は[" + o.a + "]");
	}

	private static void methodA(Obj o) {
		o = null;
	}
}

実行結果

oが持つ変数aの内容は[2]

methodA で o に null を代入しているのにもかかわらず、呼び出し元に戻って o の変数 a を表示したら、methodA を呼び出す前の値が表示できます。

つまり、参照そのものを渡しているのではなく、参照をコピーしてから渡しているのです。
なので、「参照の値渡し」と理解しておくのが良いと思います。

ちなみに C++ でオブジェクトの値渡しというと、オブジェクトをまるまるコピーしてから渡すので、C++ 経験者に対して「Java
はすべて値渡し」っていうと、思いっきり勘違いされそうな気がします。

2017年4月9日追記:
下の記事で、「参照の値渡し」という言葉がディスられています。
qiita.com

そして「参照の値渡し」

このような Java にかかわる「参照」という用語の使われ方に混乱した人たちや、混乱を治めようとする人たちが、「参照の値渡し」とか「共有渡し(call by sharing)」なる用語を開発しています。
しかし、「参照の値渡し」という人たちの、「参照の値渡し」にという用語における『参照』とはどのようなものを想定しているのかを考えるにつけ、Java において『「参照」といえば「参照値」という「値」』という前提をむしろ理解しにくくしているような気がします。

まあ確かにおっしゃるとおりで、私は「参照の値」を「参照」と省略して、当記事で使っています。

正確に言う場合は「参照値の値渡し」という言葉を使ったほうが平和を保てるかもしれません。

即戦力にならないといけない人のためのJava入門(Java 8対応) エンタープライズシステム開発ファーストステップガイド (CodeZine BOOKS)

即戦力にならないといけない人のためのJava入門(Java 8対応) エンタープライズシステム開発ファーストステップガイド (CodeZine BOOKS)

javaの引数 プリミティブ型(基本型)は値渡し

丁寧に説明しているサイトはいっぱいあるので、結論をメモしておきます。

プリミティブ型(基本型)の引数は値渡しです。

public class Argument {
	public static void main(String[] args) {
		int a = 2;
		methodA(a);
		System.out.println("変数aの内容は[" + a + "]");
	}
	private static void methodA(int a) {
		a += 1;
	}
}


実行結果

変数aの内容は[2]

methodA で引数 a を +1 しているにもかかわらず、呼出し後に a を表示させると methodA を呼び出す前の値が表示されます。これは、methodA に引数を渡すとき、そのまま渡すのではなく、値をコピーしてから渡してるからなのですね。うん、これが値渡し。

参照型(オブジェクト)の引数についてはこちらの記事を参照してください。

即戦力にならないといけない人のためのJava入門(Java 8対応) エンタープライズシステム開発ファーストステップガイド (CodeZine BOOKS)

即戦力にならないといけない人のためのJava入門(Java 8対応) エンタープライズシステム開発ファーストステップガイド (CodeZine BOOKS)

Spring Framework で管理されるオブジェクトはデフォルトでシングルトン(singleton)

タイトルに書いたとおりだが、Spring を使用する上でこれを常に頭に入れておかないと、とんでもないバグを仕込んでしまう。

実験してみる

Spring のバージョンは 4.3.7 を使用しているが、他のバージョンでも、これに関しての仕様は同じ。

フォルダ構成。

f:id:yyama1556:20170409130009p:plain

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.springframework.samples</groupId>
  <artifactId>ProjectX</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <properties>
		<!-- Generic properties -->
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<maven.compiler.source>${java.version}</maven.compiler.source>

		<!-- Spring -->
		<spring-framework.version>4.3.7.RELEASE</spring-framework.version>
	</properties>
	
	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
	</dependencies>	
</project>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="org.yyama.bean" />
</beans>

ClassA は空っぽ。 @Component だけ付けている。

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class ClassA {

}


Main クラス。

package org.yyama.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.yyama.bean.ClassA;

public class Main {
	public static void main(String... args) {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		ClassA class1 = context.getBean(ClassA.class);
		ClassA class2 = context.getBean(ClassA.class);

		if (class1 == class2) {
			System.out.println("2つのインスタンスは同じものです。");
		} else {
			System.out.println("2つのインスタンスは異なるものです。");
		}
		context.close();
	}
}

ClassA のインスタンスを 2 回取得し、それが同じものなのか別のものなのか判定している。

実行するとこうなる。

2つのインスタンスは同じものです。

このように、Spring のコンテナは同じインスタンスを複数回渡してくる。

例えば、インスタンス変数にユーザーごとの状態を保持しておいたりすると、あるユーザへの変更が全ユーザに反映されてしまう。単体テストでは発見しにくいバグを作り込んでしまうので注意が必要だ。

呼び出すたびに別のインスタンスが欲しい場合

ClassA に Scope アノテーションを付与し、パラメータに "prototype" を指定する。

package org.yyama.bean;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class ClassA {

}

これで main メソッドを実行すると、結果はこうなった。

2つのインスタンスは異なるものです。

"prototype"のほかに"singleton"があり、これがデフォルトで適用される。Scope アノテーションを省略するとシングルトンになる。

Spring の web アプリケーションでは、"request"や"session"も使用できる。"global-session"というのもあるらしいが、今のところ興味ない。

まとめ

基本的なことなので、これを知らずに開発している人がいたら教えてあげましょう。これ大事です。

↓↓↓ Spring Framework 関連の記事をまとめました。
yyama1556.hateblo.jp

「プロになるためのWeb技術入門」 ――なぜ、あなたはWebシステムを開発できないのか

「プロになるためのWeb技術入門」 ――なぜ、あなたはWebシステムを開発できないのか

Spring 関連記事へのリンク集

Spring に関連した記事を書き散らかしていたので、ここにまとめておく。