質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

16581閲覧

Java8のラムダ式での変数参照について(その2)

kidss

総合スコア12

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2015/04/13 15:42

前回の質問でも書きましたが、ラムダ式において外側で定義されているローカル変数を使う時は、「ローカル変数に暗黙にfinalが付いているものと見なされる」ため、その変数に再代入を行うとコンパイルエラーになるという事は理解できました。

たまに、Java8のstream()APIによって、for文を全て置き換えるという記事を見たりしますが(私も出来れば置き換えたい)実際は例外処理がうまく扱えなかったり、外側で定義した変数が実質的finalであったりと、難しいんじゃないかと思えてきました。ListやMapには値を追加出来るとしても、私の認識としては、変更出来る事自体がおかしく、final変数は変更しない運用の方がいいのではないかと思っています。

例外は非検査例外を使えばなんとかなりそうですが、特に変数に関してはどうしようもないのかなと思い初めてます。

前置きが長くなりましたが、Javaの経験が長い方、Java8を理解出来ている方の意見として、for文を全て置き換えるような事は可能なのでしょうか?

可能なのであれば、特に外側で定義された変数の扱いなどをどうされているのか非常に興味がありますので教えて頂きたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ラムダでのforの置き換えに限らない話ですが、不変オブジェクトを中心にプログラミングするスタイルが現在注目されています。この設計指針はマルチコアCPUを活用するという観点では非常に有効なのです。

これに対してオブジェクトの値を変更するとその全ての参照元に伝播すること(副作用といいます)を活用してGUIプログラミングが為されてきたという事情もあります。副作用を活用することが便利な状況では活用すればよいと思いますが、そのようなケースでも副作用を積極的に利用する箇所以外は出来るだけ不変オブジェクトを中心にプログラミングするほうがバグの発生を抑えられるという経験則が広がっているかと思います。

「外側で定義された変数の扱い」とおっしゃっている部分というのは変数の副作用に係る部分かと思います。排除することも出来ますが、それによるメリットが少なければ無理をして排除する必要もありませんし、副作用を活用しているのだという部分については容易に置き換えることは難しいでしょう。

投稿2015/04/14 07:12

nagise

総合スコア87

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kidss

2015/04/14 15:21

回答ありがとうございます。不変オブジェクトの考え方はあまりしたことなかったです。副作用に関しても非常に勉強になりました。
guest

0

ベストアンサー

外側で定義された変数については、私はfinalにすることは抵抗がありませんので、普通に使います。
デフォルトがfinalなら良かったのにと思っているくらいです。(関係ありませんが、例えばRustという言語ではデフォルトで不変ですがmutというキーワードで可変の変数が作れます)
とはいえ、すべての変更しない変数にfinalを付けるのは嫌なので、必要最低限のものにつけるようにしています。なので、"effectively final"の機能は歓迎しています。

次に、for文をforEachについていくつか考えてみます。

拡張for文(for-eachループ)で使用できるIterableインターフェイスを実装していれば、ストリームでなくてもforEachメソッドが使えます。

Iterable - Java SE 8 APIドキュメント

配列は直接forEachが使えません。
最近のJavaでは配列を使わないほうが良いとされていますが、旧来のAPIを使うと配列を使わざるを得ない場合もありますので、その場合はArrays.asList()を使ってListに変換することができます。Stream.of()を使ってストリームにすることもできます。
これは無理にforEachにしなくても良いと思います。

lang

1// import java.util.Arrays; 2// import java.util.stream.Stream; 3 4String[] sa = { "aaa", "bbb", "333" }; 5Arrays.asList(sa).forEach(System.out::println); 6Stream.of(sa).forEach(System.out::println);

あるリストを同じ条件で加工して新しいリストを作る処理(map)は、ストリームで書くと1行で書けます。これはforEachを使いたいケースですね。
もうちょっと簡潔に書けたらなお良かったのですが。

lang

1// import java.util.ArrayList; 2// import java.util.Arrays; 3// import java.util.List; 4// import java.util.stream.Collectors; 5 6// 従来のやり方 7List<String> a = Arrays.asList("aaa", "bbb", "333"); 8List<String> adash = new ArrayList<>(); 9for (String s : a) { 10 adash.add(s.toUpperCase()); 11} 12System.out.println(adash); // => AAA, BBB, 333 13 14// ストリーム版 15List<String> b = Arrays.asList("aaa", "bbb", "333"); 16List<String> bdash = b.stream().map(String::toUpperCase).collect(Collectors.toList()); 17System.out.println(bdash); // => AAA, BBB, 333

拡張for文で書けないようなケース、例えばカウンター変数を使うようなケースは、置き換えるのに抵抗がありますね。
(例えばScalaという言語ではzipWithIndexメソッドが標準で使えますが、Javaではタプルが無いので実現が難しいですね。)

lang

1// import java.util.Arrays; 2// import java.util.List; 3// import java.util.concurrent.atomic.AtomicInteger; 4 5// 従来のやり方 6List<String> a = Arrays.asList("aaa", "bbb", "333"); 7for (int i = 0; i < a.size(); i++) { 8 System.out.printf("%d:%s ", i, a.get(i)); 9} // => 0:aaa 1:bbb 2:333 10 11// ストリーム版 12AtomicInteger count = new AtomicInteger(); 13a.forEach(x -> { 14 System.out.printf("%d:%s ", count.getAndIncrement(), x); 15}); // => 0:aaa 1:bbb 2:333

ということで、すべてのfor文を置き換える必要は無く、ケースバイケースで良いんじゃないかと思います。

投稿2015/04/14 04:47

argius

総合スコア9390

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kidss

2015/04/14 15:19

回答ありがとうございます。サンプルも書いていただいて大変分かりやすかったです。図々しくもついでに1点追加で質問したいのですが、、、 finalで宣言した変数について ラムダ式に限った事ではありませんが、finalで宣言した不変オブジェクト以外については状態変更が出来てしまいますが、この点に関しては特に問題ないと思われますか?今までfinalを付ける変数に関しては、状態変更すらしない前提でやっていましたが、色々考えると代入さえ出来なければ問題ないようにも思ってきています。 ```lang-java final List<String> names = new ArrayList<>(); ... names = new ArrayList<>(); // finalなので代入はNG names.add("hoge"); // オブジェクトの状態変更なのでOK ```
argius

2015/04/14 23:32

Javaに限って言えば、まだ可変オブジェクトを排除するスタイルを完全には適用できないと思っています。 回答に書いたmapの例のように、適用できるパターンがあってそれを使えばコードがすっきりするなら使って、コードが却って複雑になるようなら可変オブジェクトを使えば良いと思いますよ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問