エイリアスバグ

2016年11月14日

エイリアスが発生するのは、1つの参照場所以上の箇所から同じメモリ位置にアクセスされたときです。これは多くの場合良いことではありますが、予期しない方法で起こると混乱の種となるバグにつながる場合があります。

バグの単純な例を以下に示します。

Date retirementDate = new Date(Date.parse("Tue 1 Nov 2016"));

// this means we need a retirement party
Date partyDate = retirementDate;

// but that date is a Tuesday, let's party on the weekend
partyDate.setDate(5);

assertEquals(new Date(Date.parse("Sat 5 Nov 2016")), retirementDate);
// oops, now I have to work three more days :-(

ここで起こっていることは、代入を行うと、partyDate変数が、retirement dataと同じオブジェクトへの参照に代入されるということです。その後、そのオブジェクトの内部(setDateを使用して)を変更すると、両方の変数が同じものに参照しているため、どちらも更新されます。

エイリアスはその例では問題ですが、他の状況では期待どおりに機能します。

Person me = new Person("Martin");
me.setPhoneNumber("1234");
Person articleAuthor = me;
me.setPhoneNumber("999");
assertEquals("999", articleAuthor.getPhoneNumber());

このようなレコードを共有することは一般的であり、変更があれば、すべての参照で変更されます。これが、意図的に共有する[1]参照オブジェクト、およびこの種の共有更新動作を望まない値オブジェクトについて考えるのに役立ちます。値オブジェクトの共有更新を回避するための良い方法は、値オブジェクトを変更不可にすることです。

もちろん、関数型言語はすべてを変更不可にすることを好みます。したがって、共有される変更が必要な場合は、原則ではなく例外として処理する必要があります。変更不可は便利な特性であり、これによりさまざまな種類のバグを作成することが困難になります。ただし、変更が必要な場合は、変更不可が複雑さに繋がる可能性があるため、それは決して無料の朝食ではありません。

謝辞

グレアム・ブルックスとジェームズ・バーニーの社内メーリングリストに関するコメントは、この記事を書くきっかけとなりました。

関連資料

エイリアスバグという用語は、しばらく前から使用されています。エリック・レイモンドのジャルゴンファイルに、生のメモリアクセスによってさらに不快感を生むC言語の文脈で記載されています。

ノート

1: エバンス分類には、参照オブジェクトの一般的な形式であるエンティティの概念があります。