執筆者: オキリョウ
最終更新: 2021/11/05
プログラミングできる人からしたら何言ってんだという内容かもしれませんが、結構ショックを受けたので残しておきます
先日、Kotlinのコードを読んでいたら、こんな感じのコードに出会いました
class Foo(private var value: String) {
operator fun plus(other: Foo): Foo {
return Foo(this.value + other.value)
}
}
パッと見普通のコードなのですが、このコードのために頭を抱えさせられました
というのも、別のインスタンスのprivateプロパティにアクセスするコードが書かれているからです
class Foo(private var value: String) {
operator fun plus(other: Foo): Foo {
return Foo(this.value + other.value)
}
}
例えば以下のようなコードが書けてしまいます
fun main() {
val foo1 = Foo("Hello")
val foo2 = Foo("World")
println(foo1 + foo2)
}
結果
Foo: { value : HelloWorld }
このコードでは、foo1とfoo2は、もともとのクラスは同じですが、全く別のインスタンスです。
しかし、このように書くことで、別のインスタンスのprivateなプロパティにもアクセスできるようになっています
そこで調べてみたところ、以下の記述がありました(参照先)
private
はそのクラス内(そのすべてのメンバーを含む)でのみ見える
よく見るとインスタンスではなく、クラス内のみアクセス可能と書いていました
つまり同一のクラスからできたインスタンスだとアクセス可能らしいです
だから最初のコードでもコンパイル&実行できるということらしいです
しかし、ここでいくつかの疑問が出てきます。そこでそこらへんも調べてみました
同じように動くようです(参考先)
動きません
仮に、サブクラスも参照可能にするprotectedを利用しても不可能でした
open class Foo(protected var value: String)
class Bar(private val name: String): Foo(name){
// 引数のクラスの参照はNG
fun ng(other: Foo) {
println(other.value)
}
// 自身の親の参照はOK
fun ok() {
println(value)
}
}
無理です
open class Foo(private var value: String)
// NG
fun Foo.print(){
println(this.value)
}
これはJavaにコンパイルされたときのコードを見ればわかります
public static final void print(@NotNull Foo $this$print) {
// Something...
}
Javaには拡張関数は存在しないため、引数としてクラスを渡して、それで処理しています
ですので、privateプロパティにはアクセスできないので、動きません
少なくとも公式からは出されていない感じがします(privateが一番厳しい制約とのこと)
Kotlinの場合、DelegateとかReflectionを使えば書ける気もしないこともないですが、書いたところでコンパイルエラーになりません
したがってほとんど意味を成しません
ですので、コメントやアノテーションなどで、分かるようにするのが精いっぱいだと思います
using System;
public class Program
{
public static void Main()
{
Foo foo1 = new Foo("Hello");
Foo foo2 = new Foo("World");
Console.WriteLine(foo1.getValue(foo2));
}
}
public class Foo
{
String value;
public Foo(String value)
{
this.value = value;
}
public String getValue(Foo other)
{
return other.value;
}
}
結果
World
動いています
<?php
$foo1 = new Foo("Hello");
$foo2 = new Foo("World");
print $foo1->getValue($foo2);
class Foo
{
private string $value;
function __construct(string $value)
{
$this->value = $value;
}
function getValue(Foo $other): string
{
return $other->value;
}
}
結果
World
動いています
ですので、Java、C#、PHPで動くため、おそらくほぼすべてのプログラミング言語はこの仕様だと考えられます
最初見たときは本当に驚きましたが、冷静になって考えるとこちらの方が理にかなっていますね
同一インスタンスじゃないとアクセスできない!としないといけない場面てほとんどないでしょうし
むしろこちらの方が、クラスの役割をきっちりかけていい感じがします
今までずっと勘違いしてきたことを今更知って本当に恥ずかしいです・・・
この人が書いた記事