scalaの継承ではまった話
scalaの継承ではまった話
scalaというか,JVM一般にいえることなのだろうけど,変なところではまって数時間を無駄に過ごした.
継承を用いたポリモフィズムは一般的であるが,最近は型クラスのマッチングなどを用いて処理していたりして,流行でない感がする.
そんな中どうでもいい,処理ではまったので紹介する.
派生クラスでoverrideすると基底クラスの同名の変数がnullになる
何を言っているかわからないと思うが以下のコードで再現できる.
package MAIN class Base{ val hoo = "Base" val init = { hoo.split('a') // hoo is null, so throw exception } } class Child extends Base{ override val hoo = "Child" //Base object's instance will be null val bar = "Base" def p() = { println(bar) println(hoo) } } object MAIN { def main(args: Array[String]) = { val c = new Child() c.p() } }
とりあえず,回避法を考えてみた (xuweiさんよりご指摘をいただいた部分)
try...catchステートメントを使いたくなかったので,lambdaで回避する方法を紹介.
lambdaでメンバ変数をキャプチャして,プロシージャを無理やり継続させる.
package MAIN class Base{ val hoo = "Base" val init = { ((x:String)=> if(x != null) x.split('a') else null )(hoo) //これはscala的には関数にするかlazy valにしたほうがいいらしい } println(init) //null is output } class Child extends Base{ override val hoo = "Child" //Base object's instance will be null val bar = "Base" def p() = { println(bar) println(hoo) } } object MAIN { def main(args: Array[String]) = { val c = new Child() c.p() } }
try...catchステートメントを使わず,プロシージャは継続可能になったが,,,
xuweiさんのご指摘による修正
詳しくはコメントを抜粋していただけると助かる.
package MAIN class Base{ val hoo = "Base" lazy val init = { hoo.split('a').toList } println(init.mkString("!")) } class Child extends Base{ override final val hoo = "ChildFromBase" //ここ修正 finalとは,,,いったい val bar = "Base" def p() = { println(bar) println(hoo) } } object MAIN { def main(args: Array[String]) = { val c = new Child() c.p() } }
C++とかほかの言語も気になる
class CBase{ public: std::string hoge; CBase():hoge("Base foo"){ std::cout << hoge << std::endl; } virtual ~CBase(){}; }; class CChild: public CBase{ public: std::string hoge; CChild():hoge("Child foo"){ std::cout << hoge << std::endl; } }; int main(int argc, char const *argv[]) { /** * @brief keishou test * */ auto cb = std::shared_ptr<CBase>(new CBase()); auto cc = std::shared_ptr<CChild>(new CChild()); }
コンパイラの設定は下記の通りである.かなり,ワーニングを殺しているが,下記の状態では,何も警告は出なかった.
g++-4.8 -std=c++11 core.cpp -o a.o -I./include -Wl,-rpath,./lib -L./lib -lpthread -I/usr/include/boost -Wno-deprecated -Wno-return-local-addr
このコンパイルされた内容で,問題なく動作することを確認した.
gkobayas@ubuntu:~/dev/YUTORI/YUTORI$ ./a.o Base foo #基底クラスのインスタンス作成時のコール Base foo #派生クラスのインスタンス作成時のコール Child foo #派生クラスのインスタンス作成時のコール
結論
scalaの入門にも立てていなかったので精進する.精進したところで言語仕様が理解できるかは不明だけども. 独習はやめるべきかな,,,