メソッドチェーンは、オブジェクト指向プログラミング言語において複数のメソッド呼び出しを行う際によく用いられる構文です。各メソッドはオブジェクトを返すため、中間結果を格納するための変数を用意することなく、単一のステートメントでメソッド呼び出しを連鎖させることができます。
メソッドチェーンを用いることで、各中間ステップごとに余分な変数が不要になります。開発者は、変数に名前を付けたり、その変数を記憶したりするという認知的負担から解放されます。
メソッドチェーンは、メソッドを連結するにつれて同じ行に次々と現れるメソッドの数が増えるため、「列車事故」を引き起こすと言われてきました。[ 2 ]
同様の構文としてメソッドカスケードがあり、メソッド呼び出し後に式がメソッドの戻り値ではなく現在のオブジェクトに評価されます。メソッドが現在のオブジェクト自体を返すようにすることで、メソッドチェーンを使用してカスケードを実装できます。カスケードは流暢なインターフェースの重要なテクニックであり、オブジェクト指向言語ではチェーンが広く実装されているのに対し、カスケードはそうではないため、この「これを返すことでチェーンしてカスケードする」形式は、単に「チェーン」と呼ばれることがよくあります。チェーンとカスケードはどちらもSimula言語に由来します。
チェーンは構文上の制約ではありますが、意味論的な意味合いも伴います。つまり、メソッドはオブジェクトを返す必要があり、チェーンによるカスケード処理を実装する場合は、返されるオブジェクトは現在のオブジェクトでなければなりません。これにより、戻り値がエラー値を返すなど、他の目的に使用されることを防ぎます。
一般的な例としては、C++std::istreamのとstd::ostream(入力ストリームクラスと出力ストリームクラス)があり、たとえば は左側のオブジェクトを返し、チェーン処理を可能にします。[ 3 ]<<
a << b << c ;// これは以下と同等です: a << b ; a << c ;関数型プログラミングは、高階関数を含むメソッドチェーンの使用例のもう 1 つです。オブジェクト指向プログラミングでは、高階関数とコレクションの操作は、連鎖したメソッドによって表現できます。[ 4 ]
Javaにおけるそのような例の一つは、以下の組み込みメソッドを使用するものですjava.util.stream.Stream<E>。
import java.util.Arrays ; import java.util.List ; import java.util.streams.Collectors ;List <Integer> numbers = Arrays.asList ( 10 , 25 , 30 , 45 , 60 , 75 , 90 ) ;List < Integer > result = numbers . stream () // .stream() は java.util.stream.Stream を返します。 . filter ( n -> n > 30 ) . map ( n -> n * 2 ) . sorted () . collect ( Collectors . toList ());Javaではfilter()、、、map()はsorted()前のリストの新しいシャローコピーを返すことに注意してください。ただし、リストをその場で操作するには、sort()メソッドを使用できます。[ 5 ]
C#の言語統合クエリ(LINQ) は、メソッドチェーンを多用しています。[ 6 ]
using System ; using System.Collections ;// メソッドチェーンを使用: IEnumerble < Foo > results = list . Where ( x => x . Bar < 10 ) . Select ( x => new { x . Bar , x . Qux });結果.ForEach ( x => { Console.WriteLine ( x.ToString ( ) ) ; } ) ;// LINQキーワードを使用: IEnumerable < Foo > results = from x in list where x . Bar < 10 select new { x . Bar , x . Qux };foreach ( Foo result in results ) { Console.WriteLine ( result ) ; }C++20 は、ライブラリとの LINQ スタイルのチェーン操作のための「パイプ演算子」として利用されます。には、 を介して呼び出されるいくつかのクラスが含まれています。[ 7 ]operator|std::rangesstd::viewsoperator()
import std ;using std :: vector ; using std :: ranges :: to ; using std :: views :: filter ; using std :: views :: transform ;vector < int > numbers = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };// パイプライン: 偶数をフィルタリングし、2 倍にして、結果を合計します。vector < int > result = numbers | filter ([]( int n ) -> bool { return n % 2 == 0 ; }) | transform ([]( int n ) -> int { return n * 2 ; }) | to < vector > ();同様に、C++26std::executionライブラリ(実行または送信者/受信者フレームワーク)は、次の連鎖によって非同期のデータ並列送信者/受信者グラフを構築します。[ 8 ]operator|
import std ;using std :: optional ; using std :: tuple ; using std :: execution :: just ; using std :: execution :: sender ; using std :: execution :: then ;sender auto work = just ( 6 ) | then ([]( int x ) -> int { return x * 7 ; }) | then ([]( int x ) -> int { std :: println ( "7 を掛けた後の結果: {}" , x ); return x ; });int answer = co_await std :: move ( work );ビルダーパターンは、コンストラクタ内で直接オブジェクトを構築するのではなく、メソッド呼び出しによってオブジェクトを構築することに依存しています。たとえば、java.lang.StringBuilderメソッドチェーンを使用してオブジェクトを構築し、最終的に呼び出しによってjava.util.StringBuilderオブジェクトに戻します。[ 9 ]java.util.StringtoString()
String result = new StringBuilder () .append ( "Hello, " ) .append ( " world !" ) .insert ( 0 , "Greeting: " ) .replace ( 10 , 18 , " beautiful " ) .toString ( ) ;Fluentインターフェースパターンは、メソッドの連鎖を実装するために完全にメソッドチェーンに依存しています。[ 10 ]
C++ では、メソッドが を返すようにすることで、Java や C# と同様のメソッドチェーンを実現できます (たとえば、クラス の場合StringBuilder、append()と はreplace()型を持ちStringBuilder&、 を返すことができます)。ただし、C++ では、返される参照が親クラスへのものである場合、潜在的なオブジェクト スライシングに注意することが重要ですが、Java、C#、Rustなどの他の言語ではこの問題は発生しません。[ 11 ] [ 12 ]*this
• ローカル宣言の構文糖衣 - let x = e1 in e2 は (λx.e2) e1 の省略形です
{{cite book}}:|website=無視されました (ヘルプ)