はじめに
ここ数年の開発でいくつもの言語を扱って開発をしていて、僕自身が混乱することがあったので各言語の構文をメモしておく。
ちなみに、ここ1年では
Swift < Objective-c < Ruby < Java
の優先順位で各言語触っていた。
どうでも良いが僕の好みの順番は
Swift < Ruby < Objective-c < Java
である。
言語構文的にはJavaは結構好きなのだが、当時はJava8が出ていなくブロック構文・匿名関数が使えなかったためJavaは面倒な言語という位置付けになってしまった。
この記事に書いているプログラムはコンパイラを通してないので、細かいところでミスがあるかもしれません。
その辺りは暖かい指摘をお願いします。
Swiftは2015年末にオープン化される予定です。
そうなると今まではサーバーサイドはJavaやPHP、Rubyなどが主力でしたが、今後はSwiftも一つの選択肢になるかもしれません。
そういった意味でも、Swiftを触ったことがない人がSwiftを知る機会になればこの記事を書いた甲斐があります。
Objecive-cはARC環境での記載になってます。
また、64bitをターゲットとした記載になってます。(32bitだとワーニングが出る箇所があります。)
Javaに関しては、特に記載がないものはJava6での構文です。
(基本構文は最新のJava8でも変わらないと思いますが、僕はJava8を触ったことがないのでJava8に関しては役に立たないかもしれません。)
P.S.
コードを入れながら記述をしているので、とても長い文章になってます。
クラスの書き方
簡単な言語の紹介も兼ねてオブジェクト指向の基本となるクラスの書き方を紹介します。
なお、RubyとJavaはガベージコレクション(GC)があるためデストラクタが存在しません。
SwiftとObjective-cにはデストラクタはありますが、ARC環境ではあまり出番がありません。
(ただ、デストラクタはあると便利な局面もある。)
※finalizeはデストラクタとは別なので、ここでは言及しません。
Swift
最後の;
は付けても付けなくてもコンパイルできます。
通常は付けませんが、JavaやObjective-cの名残で付けても問題ありません。
class Hoge : 継承元クラス {
init() {
// コンストラクタ
}
func foo() {
print("foo")
}
deinit() {
// デストラクタ
}
}
Objective-c
以下に記載しますが、Objective-cはヘッダーファイルと実装ファイルを別に持ってます。
非公開(Private)に使うならヘッダーファイルがなくてもクラスを定義することができます。
@interface Hoge : 継承元クラス
- (id)init;
- (void)foo;
@end
@implementation Hoge
- (id)init {
// コンストラクタ
return [super init];
}
- (void)foo {
NSLog(@"foo");
}
- (void)dealloc {
// デストラクタ
}
@end
Ruby
Rubyの構文は本当に無駄がなく素晴らしい言語だと思います。
class Hoge < 継承元クラス
def initialize
# コンストラクタ
end
def foo
p "foo"
end
# デストラクタは構文にはない。
end
Java
EclipseなどのIDEを使って書くとかなり気持ちがいい言語ですが、かなり記述力の多い言語です。
Objective-cも記述量が多いですが、アプリ開発という視点で見た場合はJavaの方がコード記述量が増えます。
その理由はコールバック系の処理にブロック構文が使えないためです。
// Javaは基本的にpublicをつける。
// 理由は後述。
public class Hoge extends 継承元クラス {
Hoge() {
// イニシャライザ(コンストラクタ)
}
public void foo() {
System.out.println("foo");
}
// デストラクタは構文にはない。
}
オブジェクトの初期化
上記のクラスをインスタンス化する場合は以下の構文で行います。
Swift
let hoge = Hoge()
Objective-c
Hoge *hoge = [[Hoge alloc] init];
Hoge *hoge = [Hoge new]; // こっちでもOK
Ruby
hoge = Hoge.new
Java
Hoge hoge = new Hoge()
アクセス修飾子(publicやprivate,internal,(Abstract))
これらはプログラム言語で大きく扱いが変わります。
ちなみに、AbstractはJavaのみが扱えてRubyやSwift,Objective-cでは使用できません。
(例外を使った代替え策はありますが、言語でサポートされていないのでコンパイルエラーで拾うことはできません。)
Public
Rubyの場合は何も書かなければpublicが標準となります。
どの言語も「どこからでもアクセスできる」ものになります。
Objective-cにはPublicというキーワードはありません。
Publicにしたいものをヘッダーファイルに定義すればPublicとほぼ同等に扱えます。
Swift
SwiftではPublicを使う局面はあまりありません。
テストプロジェクトが別になっている場合や、フレームワークを作る場合など以外で使う機会は殆ど無いでしょう。
public class Hoge : 継承元クラス {
public func foo() {
print("foo")
}
private func privateMethod() {
print("内部アクセスしかできない。")
}
}
Objective-c
Objective-cは厳密にはPublicやPrivateという概念がありません。
Objective-cはインターフェースを外部に公開しているかしていないかという切り分けのみになります。
@interface Hoge : 継承元クラス
- (void)foo;
@end
@implementation Hoge
- (void)foo {
NSLog(@"foo");
}
// このメソッドはヘッダー.hで定義していないので、外部からアクセスすることはできない。
- (void)privateMethod {
NSLog(@"内部アクセスしかできないが、オーバーライドは可能。");
}
@end
Ruby
Rubyは何も定義しなければpublicになります。
class Hoge < 継承元クラス
def foo
p "foo"
end
private
def privateMethod
p "内部もしくは継承先でアクセスできる。"
end
end
Java
Javaは基本的にPublicを使います。
何も定義しないとInternalとなるため、別パッケージからアクセスできなくなるため基本的にpublicキーワードをつけます。
(Internalは利便性の悪さからJavaではあまり使われません。※業務コードでは殆どないのですが、フレームワークを作る際には使う機会があります。)
public class Hoge extends 継承元クラス {
public void foo() {
System.out.println("foo");
}
private void privateMethod() {
System.out.println("内部アクセスしかできない。");
}
}
internal(アクセス修飾子なし)
SwiftとJavaは何も書かなければInternal扱いになりますが、二つの言語の挙動は別物です。
(Javaのアクセス修飾子なしの状態をInternalと言っていいのかどうかはわかりませんが・・・。)
RubyとObjective-Cにはこの概念はありません。
Swift
同一ターゲット内でアクセスができます。
なので、一つのプロジェクトでアプリを作っている場合にはアクセス修飾子を書かない書き方が基本になります。
複数のプロジェクトで開発をして、ワークスペースでプロジェクトを管理する場合やテストコードを別プロジェクトで管理する(例えばQuickなどを使う場合)はPublicをつける必要があります。
なんにしても、初めて一人でSwiftを扱う場合はpublicキーワードは使わずにアクセス修飾子無しで記述するのが一般的です。(多分)
Java
Swiftとは逆で基本的にInternalは使いません。
アクセス修飾子をつけない場合はInternalとなり、同一パッケージ内からしかアクセスできないクラスやメソッドが定義されます。
Javaの開発は基本的にパッケージ管理となるため、同一パッケージのみで共有するリソースは限られるため基本的にはinternalではなくPublicを使用します。
Protected
僕がC#やJavaを学んでいた時は当たり前の概念として存在していましたが、最近の言語ではあまり見かけないアクセス修飾子です。
自分のクラスと継承先でのみ使用する際に定義します。
主な使用用途はカプセル化してメソッドなどを隠したいが、継承したクラスでは使わせたい場合に使用します。
Javaのみに存在して、SwiftやRuby、Objective-cには存在しません。
RubyのPrivateはJavaのProtectedとほぼ同義です。
(コメントに記載されている通りで、厳密にはRubyにProtectedはあります。参考リンクを記載しておきますので、興味のある方は見てください。)
Objective-cは特殊で、ヘッダーに書かれていない継承元のメソッドは呼べないがオーバーライドは可能である。
また、実装クラスに定義されたインターフェースはprotected継承ができたりするのでprivateとprotectedの境界線が曖昧になってる。
Private
JavaとSwiftのPrivateはほぼ同義で、そのクラス内でしか呼び出せないメソッドが定義できます。
唯一違うのはSwiftの場合は同一ファイル内なら別クラスからもPrivate定義されたものを呼び出せますが、Javaでは同一クラス内(インナークラス含む)で無いと呼び出せない点が違います。
この違いが影響することはほとんどないのですが、Javaの方がオブジェクト指向を踏襲している点が見えます。
上記にも書いている通りで、RubyはPrivateで定義しても継承先で呼び出せます。
なので、RubyのPrivateはJavaのProtectedに相当します。
Objective-cは外部に公開しているか否かの定義しかないので、上記のProtectedの部分にも書いている通り厳密なPrivateは作成できません。
型宣言とジェネリクス
Ruby以外の言語は型宣言ができます。
また、SwiftとJavaではジェネリクスが使えます。
Swift
Swiftの型宣言は以下のように行えます。
また、型推論ができるため暗黙的に型を定義する事もできます。
var i = 10 // Int型として定義されます。
var i:Int = 10 // 型を定義して設定することもできます。
var i:Int? // 変数だけ宣言する場合は`!`か`?`をつける必要があります。(後述のOptionalValueを参照ください)
var str = "文字列" // String型として定義されます。
var callback:(()->())? // 関数型の変数も定義できます。
var array:[String]? // 配列型の宣言をするときには型情報をつける必要があります。
var hash:[String:String]? // ハッシュ型の宣言も同様です。
配列やハッシュを使う場合は、定義した型のオブジェクトもしくはそのオブジェクトを継承したオブジェクトしか入れることができません。
これがジェネリクスになります。(もっと深く使う事もできますが、本記事てはここまでの説明とします。)
Objective-c
NSInteger i = 10; // Objective-cでは型推論が無いため型を定義する必要があります。
NSString *str = "文字列"; // Objective-cではオブジェクト型には`*`をつける必要があります。
NSArray *array;
NSDictionary *hash;
Objective-cにはジェネリクスはありません。
Ruby
型宣言が存在しません。
ただし、型は存在します。代入したオブジェクトの型になるため再代入すると変数の型が変わることがあります。
Java
int i = 10; // Javaには型推論が無いため型を定義する必要があります。
String str = "文字列"; // Objective-cとは違い`*`は不要です。
List<String> array; // 配列の型を指定できます。Java1.5からの機能になります。
Map<String, String> hash;
変数
Swift
後述しますが、letで宣言すると定数になります。
また、型推論型なので型を記載する必要はありません。
// グローバル定数
var i = 10
var i:Int = 10 // 型情報も書く場合はこのようになります。
var str = "文字列"
var array = ["文字列1", "文字列2"]
var hash = ["キー1": "値1", "キー2": "値2"]
※hashは{}
ではなく[]
で初期化するところに注意。
Objective-c
NSMutable**は可変のデータを保持します。Mutableが付かないもの(NSArrayなど)は定義後に変更できないオブジェクトになります。
NSInteger i = 10;
NSString *str = @"文字列";
NSArray *array = @[@"文字列1", @"文字列2"];
NSDictionary *hash = @{@"キー1": @"値1", @"キー2": @"値2"}; // ArrayやDictionaryを初期化した場合は変更不可
NSMutableArray *array = (@[@"文字列1", @"文字列2"]).mutableCopy;
NSMutableDictionary *hash = (@{@"キー1": @"値1", @"キー2": @"値2"}).mutableCopy; // 無理やり可変型に変える場合はこうする。
Ruby
i = 10
str = "文字列"
array = ["文字列1", "文字列2"]
hash = {"キー1": "値1", "キー2": "値2"}
hash = {key1: "値1", key2: "値2"} // Rubyはシンボルが使える言語なので、このようにも記載できる。(文字列とは意味合いが違うので上の構文とは別もの)
Java
Javaにはシンタックスシュガーが無いので、Arrayやhashを生成するコードが長い・・・。
int i = 10;
String str = "文字列"
List<String> array = new ArrayList<String>() {{add("文字列1"); add("文字列2"); add("文字列3");}};
List array = Arrays.asList("文字列1", "文字列2", "文字列3"); // 変更不可配列
Map hash = new HashMap<String, String>() {
{
put("キー1", "値1");
put("キー2", "値2");
}
};
定数
定数はJavaとRubyは似た挙動をしますが、Swiftでは少し違った挙動をします。
Swift
Swiftでは基本的にクラススコープの中に定数を記載しますが、クラス外に定義すればグローバル定数として定義できます。
// グローバル定数
let i = 10
let str = "文字列"
let array = ["文字列1", "文字列2"]
class Hoge {
// クラス定数
class let str = "文字列"
// インスタンス定数
let i = 10
}
他の言語と違う点はArrayとDictionary(Hash)の扱いです。
RubyやJavaではArrayやHashを定数で定義しても、定数になっているのはポインタの値だけで中身に関しては書きかえれます。
しかし、Swiftの場合はArrayとDictionaryをlet
で定義した場合は中身を書き換えることができません。
(Swiftでクラスを定義した場合は中身を書きかえれます。この辺りが少しはまりポイントです。後述しますが、関数を使うときにもこの辺りがはまりポイントになります。)
Objective-c
Objective-cではconst
を使って定義できます。
僕はあまりconstを使わないので、深くはわかりません・・・。
const NSInteger i = 10;
const NSString *str = @"文字列";
Objective-cでは定数に値を入れ直してもコンパイルエラーが発生しない気がしますが、深くは追求しません。(使うことがないと思うので)
僕は主に#define(マクロ)で定数を定義して使ってました。
(Swiftでは#defineは使えません。#ifは使えます。)
Ruby
Rubyでは頭を大文字にした場合に定数となります。
I = 10
STR = "文字列"
ARRAY = ["文字列1", "文字列2"]
# 定数に対してもデータを追加/変更できる。
ARRAY << "文字列"
Java
Javaでは厳密に定数があるわけではなく、finalキーワードを使って定義することで定数を実現します。
アクセス修飾子通りの動作をするので説明は割愛します。
final int i = 10;
final String str = "文字列";
final String array[] = {"文字列1", "文字列2"};
final private List<String> arrayList = new ArrayList<String>();
// 定数に対してもデータを追加/変更できる。
array[0] = "文字列";
arrayList.add("文字列");
for文
基本となるfor文は僕自身があまり使わない・・・。
Swift
Swiftは外側のカッコは付けても付けなくてもOK!
for文だけではなく、if文とかも同じ。
for var i = 0; i < 10; i++ {
print(i)
}
for (var i = 0; i < 10; i++) {
print(i)
}
Objective-c
NSIntgerではなくint型を使っても実現できるが、32bitと64bitの二つの環境で動かすことを考えるとNSIntgerを使うことをお勧めする。
for(NSInteger i=0; i<10; i++){
NSLog(@"%ld", i); // 32bitと64bitで変わる・・・が言及しない。
}
Ruby
あれ・・・?
Rubyって上記のような構文はない・・・?
そういえば使ったことがないや。
Java
for (int i = 0; i < 10; i++){
System.out.println(i)
}
拡張for文
forEachとかを使う場合。
基本的にこっちのfor文の方が多用されるはず。
Swift
// 以下の構文は同じ結果を返す。
for i in 0..<10 {
print(i)
}
for i in 0...9 {
print(i)
}
let array = [1,2,3,4,5,6,7,8,9]
for i in array {
print(i)
}
// Rubyのeachっぽくブロック構文を使うことも一応できる。(mapの使用用途として正しいとは言えないが。。。)
array.map {
// ブロック構文はスコープが変わるので注意
print($0)
// このコードを避けるためにeachを実装している人もチラホラと・・・。
$0
}
Objective-c
NSArray array = @[@(1), @(2), @(3), @(4), @(5), @(6), @(7), @(8), @(9)];
for (NSNumber i in array) {
NSLog(@"%@", i)
}
Ruby
for i in 1..9
p i
end
array = [1,2,3,4,5,6,7,8,9]
for i in array
p i
end
array.each { |i|
# ブロック構文はスコープが変わるので注意。
p i
}
Java
int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i: array){
System.out.println(i);
}
for文の制御
for文の処理を途中で終了するのはbreak
。
for文の頭に戻る場合はcontinue
。(rubyの場合はnext
)
rubyにはredoなどの表現もある。
Swiftはfor文にラベルをつけれるので一気にfor文の外にジャンプすることもできる。
以外と便利。
for_i: for i in 1...5 {
for j in 1...5 {
// 外側のループまで一気に抜ける。
break for_i
// 外側の次のループを行う
continue for_i
}
}
if文
if文はrubyの構文が本当に素晴らしい。
他のif文は似たり寄ったりだけど、nilの扱いなどに差がある。
Swift
flg = true
if flg {
// 処理
}
var str:String? = nil // nilの型推論は出来ないため型を定義する必要がある。
if str {
// この構文はコンパイルエラー。「str != nil」などの記述にする必要がある。
}
if let s = str {
// この構文はOK。後述
}
Objective-c
BOOL flg = YES;
fi (flg) {
// 処理
}
NSString *str = nil;
if (str) {
// 値が入っている場合に通る。
}
Ruby
flg = true
if flg
# 処理
end
str = nil
if str
# 値が入っている場合に通る。
end
# Rubyは以下のような構文も許容している。
p "処理" if flg
p "falseの場合の処理" unless flg
Java
boolean flg = true;
if (flg) {
// 処理
}
String str = null;
if (str) {
// この構文はコンパイルエラー
}
if (str != null) {
// この書式はOK
}
Switch文
Swiftでは結構使える構文なので紹介。
Swift以外は軽く紹介。
Swift
SwiftのSwitch文はEnumに対して高い効果を発揮します。
// 普通のSwitch文ではdefaultが必須になる。
// 各処理にbreakは不要。
var value = 1
switch value {
case 0:
// なんらかの処理を書かないとコンパイルエラー
print(value)
case 1,2:
// 複数の条件もかける。
print(value)
default:
// defaultは必須。
break
}
// Enumの場合は全ての値を定義した場合にdefaultが不要になる。
enum EnumTest {
case A
case B
case C
}
var enumTest = EnumTest.A
// ここで「値が決まってるからSwitch文は意味ないよ」とかのワーニングが出るがサンプルなので気にしない。
switch enumTest {
case .A:
print("A")
case .B:
print("B")
case .C:
print("B")
}
Enumでdefault
が不要ということは、後でEnumの値を追加した場合にコンパイルを使って追記する箇所を洗い出せるという特徴がある。
Objective-c
Objective-cのSwitchは非力で使い物にならない。
というのも、Int型にしか使えないからである。
NSInteger value = 0;
switch(value){
case 0:
// 処理
// breakは必須。書かないと処理が下に流れていく。
break;
case 1:
case 2:
// 複数の条件は書けないのでbreakを書かずに処理をまとめる。
break;
default:
// 任意
}
Ruby
RubyのSwitch文はSwiftとは別の意味で強力。
割愛しますが。。。(というか、僕は複雑なcase文を滅多に使わない。)
value = 1
case value
when 0
# 処理
# breakは不要。
when 1,2
# 複数の条件もかけるし、whenの後に評価式も書くことができる。
else
# 任意
end
Java
化もなく不可もなく。
Objecive-cに似てるがString等にも使用できるので使い所はある。
int value = 0;
switch (value){
case 0:
// 処理
// breakが必要
break;
case 1:
case 2:
// 複数の条件は書けないのでbreakを書かずに処理をまとめる。
break;
default:
// 任意
}
ブロック構文(匿名関数)
ここで言うブロック構文は関数に対して関数を引き渡す処理とする。
ラムダ式に関しては後述のファーストクラスオブジェクトで説明。
Android Javaではこの構文がないためリスナーが多用され、匿名クラスリスナーが増えてネストが深くなったりリスナークラスがあちらこちらに定義されてしまう。
僕はそれが嫌でAndroidアプリは敬遠している。(苦笑。他にも理由があるが・・・)
Swift
Objective-cに比べてかなりスマートに扱えるようになった。
func api(callback:(String)->()) {
// 非同期処理
}
// 処理結果を非同期で受け取る。
api() {
print($0)
}
※Objective-cもSwiftもGCが無いため(Weak参照)[http://qiita.com/syou007/items/51f2342850e408739cc9]に関して意識する必要がある。
この辺りがiOSアプリ開発のハードルを大きく上げてしまう。
Objective-c
僕はObjective-cのブロック構文についてはいつも混乱する。
以下のコードも正しいかどうかをコンパイラでチェックしたくらいだ・・・。
構文は分かりにくいが、ブロック構文が使えることがとても重要。
// クラス内に定義
- (void)api:(void (^)(NSString *result))callback {
callback(@"結果");
}
// 同クラスで呼び出す場合
[self api:^(NSString *result) {
NSLog(@"%@", result);
}];
Ruby
# 以下の二つの関数はどちらも同じ動作を行う。
def api(&callback)
# 非同期処理
callback.call("結果")
end
def api2()
# 非同期処理
yield("結果")
end
# 処理結果を非同期で受け取る。
api { |result|
p result
}
Java
Javaはバージョン8からラムダ式が入った。
そのため、僕自身はこの構文を使ったことがない。
また、AndroidアプリではJava8と等価のものはまだリリースされていないため、以下の構文は使えない。
後で書くが、Java8のラムダはファーストクラスオブジェクトでは無いのでJava8はこの中で唯一高階関数ではない。(らしい)
ただ、Rubyも厳密にはProcオブジェクトをもち回ってるのでファーストクラスオブジェクトでは無い気がするのだが・・・。こっちは高階関数らしい。(よくわからん。)
※以下のコードはJava7以下のバージョンではコンパイルできません。
import java.util.function.Function;
public class Test {
public void api(Function<String, String> callback) {
// 非同期処理
callback.apply("結果");
}
public static void main(String[] args) {
Test t = new Test();
t.api(result -> {System.out.println(result); return "戻り値を書かないとダメだった。";});
}
}
とりあえず、それっぽく動作するサンプルを書いてみたが・・・
これが正しいかどうかがわからない。
戻り値をVoid
にしたかったのだが、できなかった。
コメントの非同期処理
の部分も本当に非同期処理を書いてうまく行くのかどうかも不明。
後発の割には難しい構文だと感じた。
関数のファーストクラスオブジェクト
ファーストクラスオブジェクトとは第1級オブジェクトのことで、生成、代入、演算、(引数・戻り値としての)受け渡しができるものらしい。
平たく言って、関数を変数に持つことができてその値を受け渡せるかどうかかと思う。
それぞれの言語で行った場合を記載します。
Swift
先ほどの関数を変数に詰めてみる。
// 型推論でもできるかもしれないが、僕にはその自信がなかった。
let api:((callback:(String)->())->()) = {
// 非同期処理
$0("結果")
}
api() {
print($0)
}
// 先に型だけ定義して後で入れる場合。
var api:((callback:(String)->())->())?
api = {
// 非同期処理
$0("結果")
}
api?() {
print($0)
}
Objective-c
typedefを使わないで試行錯誤してみたが、できなかった。。。スキル不足。。orz
typedef void (^Callback)(NSString *result);
void (^api)(Callback) = ^(Callback callback) {
callback(@"結果");
};
api(^(NSString *result) {
NSLog(@"%@", result);
});
typedefを使うと結構スッキリ書けました。
Ruby
実行はしていないが、この概念であってるはず。
型が無いので一番楽に実装できた。
api = -> (&callback) {
# 非同期処理
callback.call("結果")
}
api.call() { |result|
p result
}
Java
Functionを変数に詰め込めることはわかるが、今回はコールバック処理も詰め込む必要がある。
Function<String, Integer> api = s -> Integer.parseInt(s);
System.out.println(api.apply("12345"));
このコードは思い浮かんだのだが、この次が思い浮かばない。
・・・というか思考停止。
Function<Function<String, String>, String>
という型が定義できればできると思うけど、、、Objective-cのコードを書くので疲れたのでスルー。
Javaの関数はファーストクラスオブジェクトでは無いって言ってたし、できないかもしれない。
クロージャ
クロージャとは閉じた世界の中で外の変数を使用するというものだったと思う。
コードはかけるが、説明できるほどのスキルがない。
Swift
2パターンのクロージャを書いてみる。
前者はそれなりに使うが、後者はなかなか使う機会がない・・・
// 外に定義された変数を関数の中身でキャプチャーして利用する。
var blockValue = 1
let closure = { (value:Int) in
print("\(value + blockValue)")
}
closure(10) // -> 11
// 外に定義した変数を変更して再呼び出しを行う。
blockValue = 4
closure(10) // -> 14
// 関数内に変数を定義して、関数の中の関数を返して呼び出していく。
let closure:()->(()->()) = {
var value = 1
let closure2:()->() = {
value++
println("\(value)")
}
return closure2
}
// クロージャーの中の関数を取り出す。
let closure2 = closure()
// 中身の関数を呼び出す。
closure2() // -> 2
closure2() // -> 3
Objective-c
Swiftと同様のことをObjective-cで行うと以下のようになる。
// 外に定義する値には__blockを付けます。これを付けないとこの後の変更が関数内に影響しなくなります。
__block NSInteger blockValue = 1;
// 関数を作成
void (^closure)(NSInteger) = ^(NSInteger value) {
NSLog(@"%ld", value + blockValue);
};
// 定義した関数を呼び出します。
closure(10); // -> 11
// 外に定義した変数を変更して再呼び出しを行う。
blockValue = 4;
closure(10); // -> 14
// 関数型を定義します。
typedef void (^Closure)();
// 関数を返す関数を作成。
Closure (^closure)() = ^() {
__block NSInteger value = 1;
void (^closure2)() = ^() {
value++;
NSLog(@"%ld", value);
};
return closure2;
};
Closure closure2 = closure();
closure2(); // -> 2
closure2(); // -> 3
Ruby
Rubyも同様に実装。
やはりRubyの方がスマートにかける。
blockValue = 1
closure = -> (value) {
p "#{value + blockValue}"
}
closure.call(10) # -> 11
# 外に定義した変数を変更して再呼び出しを行う。
blockValue = 4
closure.call(10) # -> 14
closure = -> () {
value = 1
closure2 = -> () {
value += 1
p value
}
closure2
}
closure2 = closure.call()
closure2.call() # -> 2
closure2.call() # -> 3
Java
そろそろ僕の非力な環境(テキストエディター)ではJava8のサンプルを作るのがきつくなってきたのでリンクだけ。
(きしだのはてな)[http://d.hatena.ne.jp/nowokay/20130522]さんのブログをみるとクロージャはJava8では使えないけど、それっぽいことまではできるとのことです。
ただ、上記のように外に出てる変数に対してアクセスするようなことが可能であるかどうかは不明です。
(多分できないです。できたとしてもfinalがつくため外の変数を書き換えることが出来ない(影響しない)のではないかと思います。)
クラスの拡張
クラスの拡張とは、すでに定義されているクラスを書き換える処理です。
Swift
extensionを使うことで実現できます。
以下のように基底クラスであるStringに新たなメソッドを追加できます。
extension String {
func test() {
print("\(self)-追加文字")
}
}
// 使用する。
var str = "文字列"
str.test() // -> 文字列-追加文字
既存のメソッドを置き換えたりもできますが、インスタンス変数を追加したりすることはできません。
また、全てのプログラムに影響するので使用する際には注意してください。
Objective-C
Swiftと同等の機能としてカテゴリーというものがあります。
決まりではないのですがカテゴリーを作る場合は基底クラス+処理名.h
という命名をするのが定石です。
// インターフェース
@interface NSString (aaa)
-(void)test;
@end
// 実装
@implementation NSString (aaa)
- (void)test {
NSLog(@"%@-追加文字", self);
}
@end
// 使用する。
NSString *str = @"文字列";
[str test]; // -> 文字列-追加文字
こちらもSwift同様でインスタンス変数を追加することはできません。
ただし、無理やりつけることは出来なくはないみたいです。
コンパイル言語でこれだけの機能を兼ね揃えている言語はなかなかないかもしれません。
Ruby
Rubyはスクリプト言語のため、クラスの拡張以外にも指定クラスに処理追加(Mix-IN)
やインスタンスオブジェクトの拡張
もできる表現力豊かな言語です。
これらについてはここでは紹介しませんが、黒魔術を使うにはもっとも適している言語かもしれません。
class String
def test
p "#{self}-追加文字"
end
end
str = "文字列"
str.test # -> 文字列-追加文字
Rubyは後で読み込まれたものの方が強いので、先に読んでいるモジュール全てを置き換えることができます。
Java
Javaではこのようなことはできません。
なので、JavaではDIコンテナという技術が発達してます。
Diとは同じインターフェースを持った実装部分を動的に変えるという開発手法です。
ただし、Java8からdefault interface
が使えるようになったので以下のような方法で自分で定義したクラスに処理を追加することはできます。
AOPのようなことはできませんが、オブジェクト指向で問題となっている横断的に処理を追加するときの手助けにはなりそうです。
public class Test implements TestInterface {
public static void main(String[] args) {
Test t = new Test();
t.test();
}
}
interface TestInterface {
default void test() {
System.out.println("test");
}
}
※調べたJavassistという技術があるみたいですが、深追いはしてません。
リフレクション
ここではリフレクションでオブジェクトのメソッドを呼び出す方法について記載します。
Swift
Swiftのリフレクションはさぞかし洗礼されているだろう・・・
と思ったのですが、実はSwiftではリフレクションはできません。
NSTimerなどを使って非同期に呼び出すことはできますが、リフレクションと呼べるかどうかが疑問です。
Swiftで行うためにはObjective-cを使ってやるのが良さそうです。
こちらに記事を書いてますので、よければ参考にしてください。
Objective-c
一番上のクラスの書き方で定義したメソッドを呼び出します。
id clazz = objc_getClass("Hoge");
SEL sel = NSSelectorFromString(@"foo");
objc_msgSend([[clazz alloc] init], sel, nil);
Ruby
一番上のクラスの書き方で定義したメソッドを呼び出します。
clazz = Object.const_get 'Hoge'
hoge = clazz.new
hoge.__send__ 'foo'
send
メソッドでも良いのだが、send
メソッドは書き換えられる恐れがあるため __send__
を使ったほうが良いとか聞いた気がする。
java
一番上のクラスの書き方で定義したメソッドを呼び出します。
Class clazz = Class.forName("Hoge");
Method method = clazz.getMethod("foo");
method.invoke(clazz.newInstance());
OptionalValue
SwiftではOptionalValueが言語仕様に組み込まれています。
そのため、他の言語には無い強固なプログラムを作ることができます。
JavaでもOptionalValueは存在しますが、言語として組み込まれているわけではなくクラスとして組み込まれているため使用すると冗長なプログラムができてしまいます。
(Java8からOptionalがサポートされているみたいなので、ちょっとだけ触れてみます。)
OptionalValueはMapやArrayの概念に少し近いところにあります。
うまく説明できるほど理解はしていないのですが、変数をラップしてnil(null)が混在しているかどうかを事前にチェックする機構になります。
具体的には以下のコードを見ていただければと思います。
なお、以下の説明で便宜上rand
という関数が定義されていることとします。
この関数は0もしくは1をランダムで返却する関数とします。
Swift
Swiftを使う理由はOptionalValueがあるからだと言っても過言ではありません。
例えば以下のようなプログラムがあるとします。
var hoge:Hoge?
hoge = rand() == 0 ? Hoge() : nil
if hoge != nil {
// hogeのインスタンスがある場合
hoge!.foo()
}
プログラム上、hogeにはnilが混入する可能性が有ります。
Swiftではそのような変数に対して直接操作出来ないようになっています。
上記のhoge!.foo
という点がそこにあたります。
この構文は「hoge
にnil
が混在していても構わないから実行しろ!」という乱暴な構文です。
var hoge:Hoge?
hoge = rand() == 0 ? Hoge() : nil
if let h = hoge {
// hogeのインスタンスがある場合
h.foo()
}
この構文は「hoge
に値が入っていた場合はh
という変数に格納してfoo
を実行しろ!」という強固な構文です。
この構文を使っている限りヌルポが発生することがありません。
もっと簡単にかけます。
var hoge:Hoge?
hoge = rand() == 0 ? Hoge() : nil
hoge?.foo()
hoge?.foo()
と書くと、hoge
の中身がnil
ではないなら実行しろという構文になります。
この構文について掘っていくと、まだまだ素敵なことができますがこれだけでもnilが混入しにくプログラムだということが分かるかと思います。
※型キャストなどにも応用できるので、知れば知るほど強固なプログラムが書けます!
Java
Java8でも同じようなことができそうなので、サンプルだけ載せておきます。
なお、動作検証はしてません。
Optional<Hoge> hoge = rand() == 0 ? Optional.ofNullable(new Hoge()) : Optional.ofNullable(null);
hoge.ifPresent(h -> h.foo());
まとめ
気軽に書いてみようと思ったのですが、クロージャ辺りから雲行きが怪しくなりました。
というのも、僕は業務でそこまでクロージャのコードを書いていないので今回のサンプルを書くためにいろいろ調べて苦労しました。
(なんせ3日がかりで書いてますからね・・・。途中でやめようかと思ったりもしましたw)
しかし、これらの内容を自分で書くことで自分の理解力も一緒に確かめれたので書いて良かったと思います。
これからSwiftをやる人が「Rubyではこう書くのにSwiftではどう書くんだろう?」と悩む時に役に立つと嬉しいです。
また、他の構文について知りたいなどがありましたらお気軽にコメントに書いてください。
(わかる範囲で)時間があるときに調べて追記していきます。
最後に
僕はフリーランスとして業務委託や受託業務を請け負ってますので、もしお仕事を頼みたいという方がいらっしゃいましたら是非
までご連絡ください。
最後まで読んでいただきありがとうございました