SwiftとObjcとKotlinとJavaとの対照表
結論
SwiftとKotlinはObjcやJavaの記述の冗長さ(いわゆる「おまじない」と表現される言語特有の呪文)を縮小し、lambda記述はパワフルで生産的だ。
ただし、Kotlinはプリミティブやオブジェクトの配列の扱いが特殊で直観的でない。
Swiftはframework実装のpropertyにoptionalが多用されているため、プログラムソースが「?(クエスチョンマーク)」「!(ビックリマーク)」だらけになってしまいマンガかラノベを見ているようだ。
更新履歴
2018.8.29更新
2018.7.18更新
・Kotlinスコープ関数(let run apply by lazy also with 拡張関数)はいったい何が異なるのか
・Swiftとkotlinの⁉(optional型、nullable)の考察
SwiftとObjcとKotlinとJavaとの対照表
Objc | Swift (4以降) | Java | Kotlin (1.2以降) | C++ | |
抽象クラス | 抽象クラスはない。 | 抽象クラスはない。 |
|
|
|
具象クラス |
|
|
|
|
|
クラス規約定義 |
|
|
|
|
クラス規約定義はない。 |
相互運用性 | bridgeファイルにObjcのヘッダーを記載することでアクセス可能となる。 | JavaからKotlinクラスにアクセスするには、Kotlinの変数やメソッドの宣言にJVM修飾子を用いる必要がある。 | Javaクラスにアクセスできる。 | ||
定数 | #define CONSTANT 13 | static public let CONSTANT = 13 // classの外で定義してもよい | final static int CONSTANT =13 |
|
|
定数(literal) | 0.1f | 0.1 | 0.1f | 0.1 | |
変数宣言時に値をセットしない変数
|
var initOnLaterVar : View! | lateinit var initOnLaterVar : View | |||
変数宣言時に値をセットしない変数
(nil)を状態フラグとして用いる場合のみ |
var nullAbleVar : Int?
この変数型を使う場合には、次のif letブロック内で参照や代入を行う。
|
var nullAbleVar : Int?
この変数型を使う場合には、次のletブロック内で参照や代入を行う。
|
|||
変数宣言時に値をセットする変数
|
var nonNullVar : String = " " | var nonNullVar : String = " "
|
|||
初回アクセス時に値をセットする |
|
|
|||
型変換する(String? → String) | var nonNullVar: String = nullAbleVar! | var nonNullVar: String = nullAbleVar!! | |||
型変換(String? → String)してproperty やmethod をinvoke する | var abc: String? = nullAbleVar?.init() | var abc: String? = nullAbleVar?.init() | |||
Smart Cast nil判定と型変換(アンラップ・String! → String)を異常終了をともなわずに行う |
or
or
or
|
or
or
or
|
|||
文字列の連結 |
|
|
|
|
|
コンストラクタ |
|
*classメンバ変数は宣言時に初期化できる。 *オーバロードできる |
クラス名と同じ名前のメソッド public ClassName() *classメンバ変数は宣言時に初期化できる。 |
|
|
デストラクタ | - (void)dealloc | deinit { } |
Javaの言語としてのデストラクタはない androidライフサイクルとしてのデストラクタ Activity#onDestroy() |
デストラクタはない。 | |
メソッド(関数) | |
|
|
|
|
クラスメソッド | >+ (void) classMethod { |
|
|
|
|
lambda(blocks, closure) |
|
or or or
|
androidSDK(Java)にはlambdaは採用されていない。 |
|
|
spawn(メインスレッド) |
|
DispatchQueue.main.async { } |
|
|
|
spawn(サブスレッド) |
|
DispatchQueue.global(qos: .default).async { } |
|
|
|
配列 | int var[3]; | var variable:[Int] = [] // 初期化 variable = [1, 3, 5] |
int[] array_var = new int[3]; int array_var[] = new int[3]; どっちでもよい。 |
プリミティブ・クラス var variable:IntArray = IntArray(3) Arrayクラス var variable:Array<Int> = Array(3, x -> x) |
|
ポインタ(参照、エイリアス、アドレスポインタ) | 基本データ型 int *pointer_var = address; オブジェクト オブジェクトはすべてポインタ |
var variable:UnsafePointer<Int> メソッド引数へのポインタ渡し(参照渡し) func method(label:inout type) -> Void { } method(&var) |
基本データ型 int[] pointer_var = new int[1]; *メソッド引数へのポインタ渡し(参照渡し)に配列を使う。 オブジェクト オブジェクトはすべてポインタ |
*メソッド引数へのポインタ渡し(参照渡し)に配列を使う。 |
|
ポインタ表現 | ポインタ *variable |
var variable:UnsafePointer<Int> variable.pointee |
配列にする int[] pointer_var |
配列にする val pointer_var:Array<Int> val pointer_var:Array<String> |
|
クラス配列の使い方 | [C++] CGPoint **var = new CGPoint*[count]; |
CGPoint var[] = new CGPoint[count]; var[i] = new CGPoint(); |
|||
インスタンス生成 (ビュー、ウィジェット、コントローラ、クラスなど) |
|
|
|
|
|
(Sub)View表示 | [view addSubView:subView]; | view.addSubView(subView) | ViewGroup#addView(subView); *ViewGroup(LayoutView)だけaddViewできる Activity#addContentView() *R.layoutをinflateしてaddContentViewする |
||
(Sub)Viewを非表示にする | [subView removeFromSuperview]; *Viewのvisible属性をfalseしてもOK |
ViewGroup viewParent = (ViewGroup)this.subView.getParent(); viewParent.removeView(this.subView); *Viewのvisible属性をfalseしてもOK view.setVisibility(View.GONE); |
|||
クラス名は大文字始まり | クラス名は大文字始まり | ||||
変数名は小文字始まり | 変数名は小文字始まり | ||||
フレームワークで定義されているクラス名 | cocoaフレームワークで定義されているクラス名はプレフィックスがついていて何に使うクラスなのかが一目瞭然(ex. UIView, NSData) | Android SDKで定義されているクラス名にプレフィックスが全くないので調べないと何に使うクラスなのかわからない(ex. View, Data) | |||
リソースファイル名 | パソコンで使えるファイル名ならOK | 小文字と数字だけ。大文字が使えない | |||
[UIColor whiteColor] | Color.WHITE 又は R.color.white |
||||
文字定数の国際化 | NSLocalizedString(@"name", @"comment") 定数はLocalizable.strings(Localization)で定義する |
getResources().getString(R.string.name) 定数は res/values/strings.xml res/values-ja/strings.xml で定義する |
|||
@“文字定数” | “文字定数” | “文字定数” | “文字定数” | ||
nibでもコードでも画面作れる | xmlでもコードでも画面作れる | ||||
application:didFinishLaunchingWithOptions: | onCreate() | ||||
メソッドスコープ | .hの@interface class に定義 | public type methodName() | |||
.mの@interface class (private)に定義 | private type methodName() | ||||
変数スコープ | .hの@interface className にプロパティ定義 @property (assign, readwrite) type varName; これによりセッター、ゲッター、インスタンス変数(_varName)を生成する |
public type varName; | |||
.mの@implementation className {}内にインスタンス変数定義 type _varName; |
private type varName; | ||||
[instanceVariable methodName] | instanceVariable.methodName() | ||||
self.instanceVariable | this.instanceVariable *this.はつけてもつけないくても同じ意味 |
||||
[array objectAtIndex:0] | array.get(0) | ||||
NSInteger, int | int | ||||
IBOutlet *view or [topView viewWithTag:] | findViewById() | ||||
@interface subClass : superClass { | class subClass extends superClass { | ||||
#import "importFileName.h" or @import className; |
import className (importするのはフレームワークのみ/自動) |
import packageName.className (importするのはフレームワークのみ/自動) |
import packageName.className (importするのはフレームワークのみ/自動) |
||
クラス名はプロジェクトで一意 | .javaでpackage名を定義すると同じクラス名でもOK、別スコープとなる | ||||
ライフサイクル | AppDelegateとviewControllerライフサイクル | ActivityとFragmentライフサイクル | |||
didFinishLaunchingWithOptions(appDelegate) | onCreate | ||||
applicationDidBecomeActive(appDelegate) | onStart | ||||
applicationWillEnterForground(appDelegate) | onResume | ||||
applicationDidEnterBackground(appDelegate) | onPause | ||||
applicationWillTerminate(appDelegate) | onStop | ||||
applicationDidBecomeActive(appDelegate) | onRestart | ||||
dealloc | onDestroy | ||||
バンドル情報(パッケージ情報)取得 | productName-Info.plist で定義した値 [[NSBundle mainBundle] infoDictionary] setObject, objectForKey etc. |
AndroidManifest.xml で定義した値 PackageInfo #packageName etc. |
|||
keyValue persistentストア(アプリ内共通preference)に保存・読み出し | preferenceファイルは1つ
|
|
|||
スマートフォン設定情報取得(ネットワーク接続) |
|
|
|||
画面の向きを固定する | plistのUISupportedInterfaceOrientations | AndroidManifestのscreenOrientation | |||
アプリ設定情報(Setting)の取得 *iOSは設定アプリ、androidはアプリ内のActionBarButtonから設定した値 |
設定アプリに設定項目を設定する Settings.bundle (Root.plist, Root.strings) に設定項目(key)を定義する NSInteger value = [[NSUserDefaults standardUserDefaults] objectForKey:keyName]; |
ActionBarButton(アプリ内のOptionMenu)表示用Activityを設定する startActivity(new Intent(getBaseContext(), PreferenceActivity.class)); PreferenceManager.getDefaultSharedPreferences(this).getString("key", "defaultValue"); |
|||
フェッチ機構 | NSFetchedResultController | LoaderManager CursorAdapter (SimpleCursorAdapter) |
|||
DBインタフェース | NSManagedObjectContext | ContentResolver | |||
データ管理 | NSManagedObject | Cursor | |||
画面遷移(画面表示) | pushViewController, presentModalViewController | startActivity() | |||
viewController間の値の受け渡し | allocしたインスタンスのインスタンス変数に値をセットしてから表示(pushViewController etc.)する。 protocol定義のdelegateメソッド引数にセットして呼び出す。 |
Intentのdata,extrasにセットしてstartActivity()する。 | |||
画面閉じる(非表示) | dismissModalViewController dismissPopOver とか |
Activity#finish() | |||
Controller - NSManagedObjectContext - CoreDataModel | Controller -> Bean -> DTO <- DAO (sql)-> DB(Entity定義) *Bean:画面UIのためのデータを扱う DTO : テーブル操作のためのデータを扱う |
||||
クラス定義(継承) | .h @interface className : superClass <protocolName> @property (retain, nonatomic) type publicVarName; - (type)publicMethodName:(type)paramA rabel:(type)paramB; @end |
.swift class className : superClass ,interfaceName { accessLevel var vName:type accessLevel methodName() -> type { ... } } |
.java public class className extends superClass implements interfaceName { accessLevel type vName; accessLevel type methodName() { ... } } |
.kt class className : superClass() { accessLevel var vName:type accessLevel methodName() : type { ... } } |
|
クラス実装 | .m |
定義と実装は同じファイル(.swift)で行う | 定義と実装は同じファイル(.java)で行う | 定義と実装は同じファイル(.kt)で行う | |
コールバック | Delegate (デリゲート) | Interface (インタフェース) Listener (リスナー) |
|||
デリゲート(リスナー)機能実装 | プロトコルを宣言して @interface className : superClass <UISearchBarDelegate> nibでデリゲートクラスを定義して クラスにメソッドを実装する - (void)finishSearchWithString: |
インターフェースを宣言して public class className extends superClass implements SearchView.onQueryTextListener コールバッククラスを定義して SearchView.setOnQueryTextListener(this); クラスにメソッドを実装する public boolean onQueryTextChange() {} |
|||
インナークラスで実装することもできる *インターフェース宣言(implements)はしなくてよい *1箇所への記述だけでよいため手軽だが分かりにくくなる恐れあり SearchView.setOnQueryTextListener(new SearchView.onQueryTextListener() { public boolean onQueryTextChange() {}; }); |
|||||
クラスのインスタンスが持つ変数 | インスタンス変数 | プロパティ、フィールド、メンバ変数 などいろんな呼称がある |
|||
インスタンス変数の初期化 | initとかのコンストラクタで行う | クラス定義で初期値を設定できる | |||
インスタンス変数使用方法 | _iVarName = someOne; // setterは走らない // retainカウント増えない self.iVarName = someOne; // setterが実行される // retainカウント増える |
iVarName = someOne self.iVarName = someOne |
iVarName = someOne; this.iVarName = someOne; // 全く同じ意味 |
||
ObjectiveCの@property指定方法 | 何も指定しないと、 クラス変数デフォルト(atomic, strong) プリミティブ変数(intなど)のデフォルト(atomic, assign) strong // このクラスで管理するインスタンス変数 weak // 他クラスで管理しているインスタンスを参照するためのインスタンス変数 nonatomic // 非スレッドセーフ(通常この指定) |
||||
デバッグ時だけログ出力 | *通常appName_Prefix.pchでプリプロセッサ定義して利用する |
|
|||
変数(valiable) | aValiable | aVariable | |||
クラスのインスタンス変数 | ClassName *instance | ClassName instance | |||
アクセスレベル(スコープ)デフォルト | 継承クラス内(protected) | パッケージ内 | |||
サーバからアプリへのプッシュ通知(リモート通知) | RemoteNotification 証明書と鍵をサーバーに置く→アプリがデバイストークンを取得→アプリからデバイストークンをサーバに送信→サーバは(APNS)Apple Push Notification Serviceに通知をプッシュ→APNSからアプリ(デバイス)に通知送信 文字列で通知する 通知形式は設定アプリでユーザが決定する |
GCM(Google Cloud Messaging for Android) 通知形式はアプリが決定する |
|||
ローカル通知(アプリがローカルデバイスに対して通知する) | UILocalNotification 通知はOSが受け取り表示する 文字列で通知する 通知形式は設定アプリでユーザが決定する |
AlarmManager 指定時間後にブロードキャストインテント発行 PendingIntent BroadcastReceiver ブロードキャストインテント受信 NotificationManager ステイタスバーに通知を表示 PendingIntent(文字列)で通知する 通知形式はアプリが決定する |
|||
plist - CFBundleURLTypes, CFBundleDocumentTypes | AndroidManifest - intent-filter | ||||
UIGestureRegognizer | GestureDitector | ||||
@interface myView : UIView <UIGestureRecognizerDelegate> { | public class myView extends Activity implements OnGestureListener { | ||||
CoreGraphics | Canvas | ||||
リソースファイル読み出し | glTexImage2D(), CGContextDrawImage |
R.idを指定するだけ | |||
Assetファイルのファイル・コピー | NSFileManager#copyItemAtURL を呼ぶだけ | InputStream#read して
*FileChannel#transferTo というメソッドもあるが、Assetファイル(AssetManager)などをパスに変換するメソッドが用意されていないため使えない。 |
|||
UI操作はメインスレッドで行う |
|
or // メインスレッドを指定してHandlerを生成 new Handler(); // 引数を省略するとメインスレッドに紐づく new Handler(Handlerに紐づくThreadのLooperを指定); *紐づくとはHandler#postする先のスレッドのQueueを規定するということ。 AsyncTask#doInBackground() { // バックグランド Handler#post(new Runnable() { @Override public void run() { // イベントキューに登録する } }); } |
|||
非同期実行 | iOSはGCDのおかげでサブスレッド作成実行が簡単
or NSThread detachNewThreadSelector or UIView#performSelectorOnMainThread UIView#performSelectorInBackground |
androidのサブスレッド作成実行手続きはiOSに比べて複雑
or AsyncTask を継承してインナークラスを作る class AsyncTaskClass extends AsyncTask<...> { } そして、非同期実行する new AsyncTaskClass().execute(); or
or |
|||
static変数(ヒープ領域にメモリ領域を確保する) | メソッド中に定義できる | クラスメンバ変数として定義する | |||
配列初期化 | type var[10]; *var = malloc(10*sizeof(type)) |
type[] var = new type[10]; | |||
NSFileManager | File (ContentWrapperを継承したクラスで使う) | ||||
オリエンテーション取得 | // デバイスの向き UIDeviceOrientation = [[UIDevice currentDevice] orientation]; // 描画されている画面の向き UIInterfaceOrientation = [UIAppliation sharedApplication].statusBarOrientation; |
this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT | |||
オリエンテーションイベント取得 | UIViewController#viewWillTransitionToSize: withTransitionCoordinator: iOS7まで willRotateFromInterfaceOrientation: didRotateFromInterfaceOrientation: |
manifestに Activity android:configChanges="orientation|keyboardHidden"すると onConfigurationChanged()が呼ばれる |
|||
テーブル(リスト)表示管理 | UITableViewController | ListActivity ListFragment *テーブルUI+表示データの管理 |
|||
セクション(グループ)数 | tableView:numberOfSectionInTableView: | CursorAdapter#getViewTypeCount() | |||
セクション・ヘッダー表示 | tableView:titleForHeaderInSection: | CursorAdapter#newView() | |||
テーブル(リスト)表示内容設定 | tableView:cellForRowAtIndexPath: | CursorAdapter#newView() |
|||
コレクション(配列) | NSArray *vName = @[]; // 空の配列 NSMutableArray *vName = [@[ @"val1", @"val2" ] mutablecopy]; // 配列の初期化 |
List<ObjectType> vName = new ArrayList<ObjectType>(); // 空の配列 List<ObjectType> vName = Arrays.asList("value1", "value2", "value3"); // 配列の初期化 |
|||
コレクション(連想配列) | NSDictionary *vName = @{}; // 空の連想配列 NSMutableDictionary *vName = [@{ @"key1" : @"val1", @"key1" : @"val1" } mutablecopy]; // 連想配列の初期化 |
|
|||
メソッド・オーバーライド | 普通にsuperClassやプロパティのメソッドを再定義すればOK | @Override public void superClassMethod() {} @Override public void otherClassMethod() {} |
|||
いろいろ使うAppDelegateやMainActivityの求め方 | [[UIApplication sharedApplication] delegate]で求める | MainActivityのonCreateでGlobalクラスのクラス変数に保存しておく *所属するFragmentやView以外からは求めるしくみは用意されていない |
|||
FragmentからActivityの求め方 | Fragment#getActivity(); | ||||
ViewからActivityの求め方 | View#getContext(); | ||||
変数型(データ型) | 変数型を見ただけで何に使われる変数かが分かる。 ex. UIDeviceOrientation orientation |
Java標準のデータ型しかない。 ex. int orientation *Javaにはプリプロセッサー(#define)やtypedefが無いため |
|||
YES, NO | true, false | ||||
配列領域確保 | float var[4]; | float[] var = new float[4]; | |||
国際化(文字定数、画面レイアウト) | 文字定数を国際化するとき |
文字定数を国際化するとき res/values-ja/strings.xmlに値を定義して getResources().getString(R.string.name)で呼び出す 画面ごとに国際化するとき res/layout-ja/activity_main.xml |
|||
機種判定 |
|
res/layout/activity_main.xml res/values/dimens.xml ↑と↓で画面サイズによって、レイアウトやパディング値を切り替える res/layout-w820dp/activity_main.xml res/values-w820dp/dimens.xml |
|||
OSバージョンチェック |
|
|
|||
文字列数値変換 |
|
|