Sunday, May 27, 2012

Functional Programming, что же лучше для Android?

Привет всем. На этот раз я определился с языком, который получше чем Java, но не настолько не подходит к Android, как Scala. Речь пойдет о моем старте библиотеки под язык Kotlin.

Давайте посмотрим на этот пример:
package com.example
// тут как обычно, ипорт и имя пакета
import android.app.Activity
import android.os.Bundle
import android.kotlin.* // это особенный импорт, моя библиотека
import android.widget.Button
import android.app.AlertDialog

// наследование от Activity и вызов конструктора ()
class HelloActivity : Activity() {

    // приватное значение для идентификации диалога.
    // по сути, будет определен тип значения исходя из "1"
    // ну и раз это val, то поменять уже нельзя будет
    private val DIALOG_EXAMPLE = 1

    // а вот мы и в onCreate. Bundle тут с "?", т.к. язык считает, что
    // savedInstanceState может быть равен null
    public override fun onCreate(savedInstanceState: Bundle?) {
        // тут все привычно, только если использовать трэйты, то надо писать
        // super<activity>.onCreate...
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)

        // пока ждем lazy properties, пишем локально, указывая тип в generic параметре
        // и ID ресурса, и результат buttonHelllo будет Button? с id R.id.button_hello
        // lazy properties, дадут возможность писать данную строку вне onCreate
        val buttonHello = findView<Button>(R.id.button_hello)

        // т.к. buttonHello может быть не найден, а значит - null,
        // то и пишем через ?.. Kotlin дает возможность, если аргумент последний (или один)
        // и является функцией, то можно убрать () и сразу писать { }
        // Собственно тут я просто обвернул View.OnClickListener в функцию
        buttonHello?.setOnClickListener { showDialog(DIALOG_EXAMPLE) }
    }

    // для создания диалога по патерну Android, нужно перегрузить эту функцию
    // тут нам поможет так же патерн match. Только со словом when и аргументом id
    public override fun onCreateDialog(id: Int) = when (id) {
        // return тут не обязателен, т.е. сразу возвращаем результат
        DIALOG_EXAMPLE -> AlertDialog.Builder(this).setTitle("Title")
            ?.setMessage("Hello World")
            // следующие два методы так же обвернуты в библиотеке
            ?.setPositiveButton("Yes", {dialog, which -> })
            ?.setNegativeButton("No", {dialog, which -> })
            ?.create()
        // else это как switch -> default
        else -> super.onCreateDialog(id)
    }
}

Как по мне, так очень наглядно, чисто и сразу ясно что написано и цель написанного. Можете представить как это выглядело бы в Java. Разумееться, если без библиотеке обверток, то разница была бы совершенно незначительной, но все же, теперь есть возможность улучшить. Пока есть одно но, но оно уже в трекере у JetBrains, называется баг. Вот такие вот кложеры как { showDialog(DIALOG_EXAMPLE) } не работают, Dalvik VM не может их найти и вываливает exception. Пока ждем, обещали профиксить.


Что касается библеотеки, так там все достаточно просто:
// View
package android.kotlin

import android.view.View
import android.app.Activity
import android.view.View.OnClickListener

// непосредственная обвертка в View.OnClickListener
fun viewOnClickListener(action: (View?) -> Unit) = object : OnClickListener {
    public override fun onClick(p0: View?) {
        action(p0)
    }
}

// функция расширения которая вызывает обвертку. Дает возможность писать над любым View объектом
// setOnClickListener { ... }
fun View.setOnClickListener(action: (View?) -> Unit) = setOnClickListener(viewOnClickListener(action))
// Activity
package android.kotlin

import android.app.Activity
import android.view.View

// немного улучшаем findViewById, как null-безопастную и плюс generic
fun Activity.findView<T: View>(id: Int) = findViewById(id) as? T

// так же обворачиваем runOnUiThread
fun Activity.runOnUiThread(f: () -> Unit) = runOnUiThread(runnable(f))
// Dialog
package android.kotlin

import android.app.Dialog
import android.view.View
import android.content.DialogInterface.OnClickListener
import android.content.DialogInterface
import android.app.AlertDialog
import android.app.AlertDialog.Builder

// Обвертка кложера в Dialog.OnClickListener
fun dialogOnClickListener(action: (dialog: DialogInterface?, which: Int) -> Unit): OnClickListener =
    object : OnClickListener {
        public override fun onClick(p0: DialogInterface?, p1: Int) = action(p0, p1)
    }

// и функции расширения
fun Builder.setPositiveButton(textId: Int, action: (dialog: DialogInterface?, which: Int) -> Unit) =
    setPositiveButton(textId, dialogOnClickListener(action))

fun Builder.setPositiveButton(text: CharSequence, action: (dialog: DialogInterface?, which: Int) -> Unit) =
    setPositiveButton(text, dialogOnClickListener(action))

fun Builder.setNegativeButton(textId: Int, action: (dialog: DialogInterface?, which: Int) -> Unit) =
    setNegativeButton(textId, dialogOnClickListener(action))

fun Builder.setNegativeButton(text: CharSequence, action: (dialog: DialogInterface?, which: Int) -> Unit) =
    setNegativeButton(text, dialogOnClickListener(action))

Я считаю это очень доступно и читабельно. Скорость компиляции приемлимая, остаеться ждать и надеяться, что язык выстрелит. На этом все, до скорого.

No comments:

Post a Comment