iOSのSwiftとAndroidのGroovy
WWDCでSwiftが発表されてTLが賑わっていますが、時を同じくして6/2〜6/4に開催されたGR8Conf Europe 2014でGroovyのAndroidサポートが発表されました。
groovy-coreに取り込まれた差分: Raw modifications to run Groovy on Android by melix · Pull Request #436 · groovy/groovy-core
さっそくAndroidアプリをGroovyで書いてみた
以前RxJavaのために書いたサンプルプロジェクトがあったので、Groovy化してみました。 と言っても、GroovyはJavaに完全な上位互換があるのでそのままでも動くので、Groovyっぽいシンタックスを使ってみました。
class ComposeMessageActivity extends Activity {
private EditText phoneNumberEditText
private EditText messageBodyEditText
private TextView remainingCharactersTextView
private Button sendMessageButton
private ListView messageListView
private ArrayAdapter<String> messageListAdapter
@Override
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_compose_message)
findViews()
setupViews()
setDummyData()
}
private void findViews() {
phoneNumberEditText = findViewById(R.id.phone_number_edit)
messageBodyEditText = findViewById(R.id.message_body_edit)
remainingCharactersTextView = findViewById(R.id.remaining_characters_text)
sendMessageButton = findViewById(R.id.send_message_button)
messageListView = findViewById(R.id.message_list)
}
private void setupViews() {
def phoneNumberText = Events.text(phoneNumberEditText)
def messageBodyText = Events.text(messageBodyEditText)
def sendMessageClick = Events.click(sendMessageButton)
messageBodyText
.map({ text -> !text.trim().equals("") })
.subscribe(Properties.enabledFrom(sendMessageButton))
def maxBodyLength = getResources().getInteger(R.integer.message_body_max_length)
messageBodyText
.map({ text -> maxBodyLength - text.length() })
.map({ remainingChars -> getString(R.string.remaining_characters_text, remainingChars, maxBodyLength) })
.subscribe(Properties.textFrom(remainingCharactersTextView))
sendMessageClick
.flatMap({
Observable.combineLatest(phoneNumberText, messageBodyText,
{ phoneNumber, messageBody -> new Message(phoneNumber, messageBody) }).take(1)})
.subscribe({ message ->
if (!message.getPhoneNumber()?.trim()?.equals("")) {
messageBodyEditText.setText("")
messageListAdapter.add(message.getMessageBody())
} else {
phoneNumberEditText.requestFocus()
}
})
messageListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1)
messageListView.setAdapter(messageListAdapter)
}
private void setDummyData() {
messageListAdapter.addAll(
(1..10).collect({
new Message("xxx-xxxx", "message ${it}") as String
})
)
}
}
ソースコード全体はこちら: rejasupotaro/rxjava-android-example もちろんPower Assertも使えますし、safe navigationも使えます。
以下の手順でローカルに最新のgroovyをインストールすることができます。
$ git clone git@github.com:groovy/groovy-core.git
$ cd groovy-core
$ ./gradlew -PskipIndy=true install
GroovyとSwiftは似てる?
巷では
なんて言われてますが、本命はGroovyだと思います。
Apple's Swift programming language inspired by Groovy
上記のサイトから引用すると、リストやハッシュの扱いは、
// Swift
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Caption",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
var emptyMap = [:]
var emptyList = []
// Groovy
def shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
def occupations = [
"Malcolm": "Caption",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
def emptyMap = [:]
def emptyList = []
ほぼ同じ。変数宣言のvarとdefの違いはあって、SwiftのletはGroovyのfinalで代用できます。 クロージャは以下のとおりです。
// Swift
numbers.map({
(number: Int) -> in
let result = 3 * number
return result
})
// Groovy
numbers.collect { int number ->
def result = 3 * number
return result
}
Swiftはpositional closure parametersで、Groovyは "it" というpseudo-keywordでもっと短く書けます。
// Swift
numbers.map({ 3 * $0 })
numbers.map({ it in 3 * it })
// Groovy
numbers.collect { 3 * it }
インスタンス生成のnamed paramtersも同じです。
// Swift
var message = Message(from: "John", message: "How are you?")
// Groovy
def message = new Message(from: "John", message: "How are you?")
Swiftの@lazyとGroovyの@Lazyも非常によく似ていて、どちらも初回アクセス時に一度だけ初期化されます。
// Swift
class DataManager {
@lazy var importer = DataImporter()
}
// Groovy
class DataManager {
@Lazy importer = new DataImporter()
}
safe navigationもあります。
// Swift
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
}
// Groovy
def john = new Person()
def roomCount = john.residence?.numberOfRooms
if (roomCount) {
println "John's residence has ${roomCount} room(s)."
}
所感
機能的にも(ウィジェットとかIntentとか)ハードウェア的にも(画面は変形するものとして開発するべきとか)言語的にも(SwiftとGroovyとか)両者の距離が近くなってきた気がします。 ただ、今回はAndroidがGroovyをサポートしたのではなく、GroovyがAndroidをサポートしたように、良くも悪くもオープンだというところが大きな違いだと思います。