android studio ver. 3.1.4
kotlin_version = 1.2.61



기존 자바 프로젝트를 kotlin 으로 변경하던 중에 새로운 문제에 부딪히게 되었습니다.


Java로 작성된 Network 모듈 코드입니다.

public class NetworkManager {

    private NetworkManager(@NonNull Context c) {
    }

    public interface OnNetworkListener<T> {
        void OnNetworkResult(String requestId, T res);
    }

    public void request(OnNetworkListener listener) {
        ResponseBase res = new ResponseBase(0, "Test!!! ");
        if (listener != null) {
            listener.OnNetworkResult("", res);
        }
    }
}


public class ResponseBase {
    
    public static final int RETURN_CODE_SUCCESS = 0;    // 성공

    public int code = 0;
    public String message = "";

    public ResponseBase(int code, String msg) {
        code = code;
        message = msg;
    }

    public boolean isSuccess() {
        return code == RETURN_CODE_SUCCESS;
    }
}

Java에서 사용시 

NetworkManager.getInstance(getBaseContext()).request(new NetworkManager.OnNetworkListener<ResponseBase>() {
            @Override
            public void OnNetworkResult(String requestId, ResponseBase res) {
                Logger.d("res , ", res.isSuccess());
                Logger.d("res , ", res.message);
            }
        });

Kotlin에서 변형시

NetworkManager.getInstance(baseContext).request {
            requestId, res ->

        }

여기까지는 문제 없이 되었지만,
값을 사용하려고 보니 아래 그림과 같이 에러가 발생을 합니다. res를 인식하지 못해서 그런거라 판단이 됩니다.
그래서 캐스팅을 어떻게 해야하나 구글링을 시작했습니다.




그래도 딱히 시원한 답을 얻지 못해서 문서를 정독해보았습니다.

https://kotlinlang.org/docs/reference/typecasts.html



최종 코드는 아래와 같이 사용하였고,
저같은 경우는 res 가 여러 케이스로 들어올수 있으므로,
2번째처럼 명시적인 사용은 오류를 일으킬 가능성이 큽니다.

override fun onCreate(savedInstanceState: Bundle?) {
	super.onCreate(savedInstanceState)

	NetworkManager.getInstance(baseContext).request {
		requestId, res ->
			if(res is ResponseBase) {   // java:: res instanceof ResponseBase
				d("res , ", res.isSuccess)
				d("res , ", res.message)
			}

			i("res 2 , ", (res as ResponseBase).isSuccess)  // java:: ((ResponseBase) res).isSuccess()
			i("res 2 , ", res.message)
	}
}





android studio ver. 3.1.4
kotlin_version = 1.2.60



AlertDialog 예제입니다.

먼저 Java 간단한 다이얼로그 예제를 작성하였습니다.


 

public class SplashActivity_j extends AppCompatActivity implements  View.OnClickListener {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);

        FrameLayout lay = findViewById(R.id.lay_push);
        lay.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.lay_push:
                AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(SplashActivity_j.this, R.style.Theme_AppCompat_Light_Dialog_Alert));
                builder.setTitle("제목");
                builder.setMessage("메시지 내용");
                builder.setPositiveButton("확인",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int id) {
                            }
                        });

                builder.setNegativeButton("취소",
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int id) {
                            }
                        });
                builder.show();
                break;
        }
    }
}

아래는 코틀린으로 변환된 코드입니다.
{ dialog , id 는
{ _ , _ 이렇게 사용도 가능합니다.

class SplashActivity : AppCompatActivity(), View.OnClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        // lay 클릭시
        val lay:FrameLayout = findViewById(R.id.lay_push)
        lay.setOnClickListener(this)
    }

    override fun onClick(v: View) {
        when(v.id) {
            R.id.lay_push -> {
                // 다이얼로그
                val builder = AlertDialog.Builder(ContextThemeWrapper(this@SplashActivity, R.style.Theme_AppCompat_Light_Dialog))
                builder.setTitle("제목(kotlin)")
                builder.setMessage("내용(Kotlin)")

                /*builder.setPositiveButton("확인") {dialog, id ->
                }
                builder.setNegativeButton("취소") {dialog, id ->
                }*/
                builder.setPositiveButton("확인") { _, _ ->
                    d("alert ok")
                }
                builder.setNegativeButton("취소") { _, _ ->
                    d("alert cancel")
                }

                builder.show()
            }
        }
    }
}





android studio ver. 3.1.4
kotlin_version = 1.2.60



switchCompat 예제입니다.

아래와 같은 이미지를 구성하기 위해서 우선 xml 을 작성하였습니다.


 


<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/lay_push"
        android:padding="20dp"
        android:foreground="?android:attr/selectableItemBackground">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="start|center_vertical"
            android:text="알림설정" />

        <android.support.v7.widget.SwitchCompat
            android:id="@+id/sc_push"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end|center_vertical"
            android:checked="true"/>

    </FrameLayout>
</android.support.constraint.ConstraintLayout>


java 코드 작성

초기화는 open 으로 작성하였습니다.
아직 코틀린 문법이 if 문을 자바 스타일로 작성하였더니 에러는 아니지만, 노란줄이 생겼어요
그래서 아래 이미지처럼 바꿔 주었어요



아래와 같이 클릭으로 토글하는 간단한 예제를 작성하여 보았습니다. 
Framelayout 을 클릭시에는 performClick 으로 토글시켜보았습니다. 
이렇게 작성을 해보니, setOnCheckedChangeListener 안에서는 워닝 메시지가 나옵니다. "RenameTo _" 
 scPush.setOnCheckedChangeListener { buttonView, isChecked -> }
 이것을 
scPush.setOnCheckedChangeListener { _, isChecked -> } 이렇게 변경을 했어요.
class SplashActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var scPush: SwitchCompat    // lateinit 지연초기화
    private var init: String = "N"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        // lay 클릭시 토글
        val lay: FrameLayout = findViewById(R.id.lay_push)
        lay.setOnClickListener(this)

        // 스위치 버튼
        scPush = findViewById(R.id.sc_push)
        scPush.isChecked = init == "Y"

        scPush.setOnCheckedChangeListener { buttonView, isChecked ->
            i("scPush isChecked , ", isChecked)
            init = if (isChecked) { // on
                "Y"
            } else {    // off
                "N"
            }
            i("scPush init , ", init)
        }
    }

    override fun onClick(v: View) {
        when (v.id) {
            R.id.lay_push -> {
                d("lay_push onClick , ", init)
                scPush.performClick()
                /*init = if(init == "Y") {
                    "N"
                } else {
                    "Y"
                }
                scPush.isChecked = init == "Y"*/
            }
        }
    }
}

결과 화면





android studio ver. 3.1.2
kotlin_version = 1.2.51

Util 과 같은 클래스 안에 static method 를 만들어 놓고,

전역에서 호출하여 사용할 수 있습니다. 코틀린은 static 이 없다고 하니, 펑션 만 만들어 줍니다.

public class Utils_j {

    public static void load(String value) {
        Log.i("trip_", "method load : "+ value);
    }
}

코틀린으로 변환 해 보았습니다.

object Utils {

    fun load(value: String) {
        Log.i("trip_", "method load : $value")
    }
}

// 실행
override fun onClick(v: View?) {
        when(v?.id) {
            R.id.iv -> {
                Utils_j.load("java test")

                Utils.load("kotlin test")

            }
        }
    }


결과




여기까지는 kotlin file 에서 kotlin file 을 접근했을 때이고,

만약 java file > kotlin file 로 접근 해서 fun 호출 하게 되면 좀 더 다른점을 볼수 있어요.
Logger 라는 코틀린 파일을 만들어서 테스트를 진행 해보았습니다. 


object Logger {

    private const val TAG = "trip_"

    /** Log Level Information  */
    fun i(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.i(TAG, buildLogMsg(*message))
    }

    /** Log Level Debug  */
    fun d(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.d(TAG, buildLogMsg(*message))
    }
}

// 자바에서 kotlin 펑션을 호출
@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv:
                Logger.INSTANCE.d("kotlin input test");
            break;
        }
    }


Logger.d 이런식으로는 사용이 안되게 되어 있고, INSTANCE 가 붙어서 싱글톤 형태여야 쓸 수 있는 의미가 됩니다.

그래서 전 그냥 Logger.d 로 쓰고 싶어요 한다면 여러가지 방법이 있겠습니다.


우선 아래 코드 처럼 object Logger { } 부분을 지우면
LoggerKt.d() 로 사용할 수가 있어요.

//object Logger {

    private const val TAG = "trip_"

    /** Log Level Information  */
    fun i(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.i(TAG, buildLogMsg(*message))
    }

    /** Log Level Debug  */
    fun d(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.d(TAG, buildLogMsg(*message))
    }
//}

// 자바에서 kotlin 펑션을 호출
@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv:
                //Logger.INSTANCE.d("kotlin input test");
                LoggerKt.d("kotlin input test");
            break;
        }
    }


LoggerKt.d() 로 사용하는 것이 불편할 수 있습니다. 제가 그래요.


Logger.d() 로 쓰고 싶다면
아래 코드 처럼 @file:JvmName("Logger") 라고 추가 해주면 Logger.d() 의 형태로 사용 할 수 있게 되었습니다.

@file:JvmName("Logger")

package example.kr.myapplication

//object Logger {

    private const val TAG = "trip_"

    /** Log Level Information  */
    fun i(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.i(TAG, buildLogMsg(*message))
    }

    /** Log Level Debug  */
    fun d(vararg message: Any) {
        if (BuildConfig.DEBUG) Log.d(TAG, buildLogMsg(*message))
    }
//}

// 자바에서 kotlin 펑션을 호출
@Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.iv:
                //Logger.INSTANCE.d("kotlin input test");
                //LoggerKt.d("kotlin input test");
                Logger.d("kotlin input test");
            break;
        }
    }





android studio ver. 3.1.2
kotlin_version = 1.2.51


변수 정의에 대해 알아보겠습니다.

먼저 java로 다음과 같이 int, String 변수를 만들었습니다.

public class Global_j {

    static int num = 3456;
    static String name = "trip";
}
코틀린으로 변환 하였습니다.

object Global {
    internal var num = 3456
    internal var name = "trip"
}

// 실행 코드
override fun onClick(v: View?) {
        when(v?.id) {
            R.id.iv -> {
                Log.d("trip_", "java num : "+ Global_j.num + " , name : "+ Global_j.name)
                Log.i("trip_", "kotlin num : "+ Global.num + " , name : "+Global.name)

            }
        }
    }
결과 화면은 다음과 같습니다.



internal 은 접근 제어자 이며, 이외에 public, private, protected 로 쓸수 있습니다. 아무것도 없으면 public 으로 인식합니다.

이외에 변수 앞에 var, val 의 차이는 아래의 이미지 처럼
var 는 수정 가능 (변수로 사용)
val 은 수정 불가 (상수로 사용) 다른 값을 넣어보려고 시도 하면 에러가 발생합니다.




val 만 적었을 경우, 원하는 동작은 하지만 warring 메시지가 나오게 됩니다.




java 호환 어쩌구 저쩌구 ...

그래서 const 를 붙여주라네요.


아래는 추가적인 부분을 출력해보았어요
var name4 에서 보면 null을 넣기 위해서는 ? 따로 추가해줘야 됩니다

object Global { internal val num = 3456 internal val name = "trip" val num2 = 5678 val name2 = "store" const val name3:String = "ts" const val num3:Int = 8888 const val num3f:Float = 1.0F const val num3d:Double = 2.0 const val type:Boolean = true //var name4:String = null var name4:String? = null } // 실행 override fun onClick(v: View?) { when(v?.id) { R.id.iv -> { Log.d("trip_", "java num : "+ Global_j.num + " , name : "+ Global_j.name) Log.i("trip_", "kotlin num : "+ Global.num + " , name : "+Global.name) Log.i("trip_", "kotlin2 num : "+ Global.num2 + " , name : "+Global.name2) //Global.num = 9999 //Global.name2 = "trip_store" Log.i("trip_", "kotlin3 num : "+ Global.num3 + " , name : "+Global.name3) Log.i("trip_", "kotlin3 num3f : "+ Global.num3f + " , num3d : "+Global.num3d) Log.i("trip_", "kotlin3 type : "+ Global.type+ " , name4 : "+Global.name4) } } }

출력





android studio ver. 3.1.2


아래와 같이 하나의 List, 반복문 하나를 작성하였습니다.

public class JavaExample { static List<String> data = new ArrayList<>(); public static void setDataLoop() { data.clear(); data.add("trip"); data.add("store"); data.add("extriber"); for (int i=0;i < data.size();i++) { Log.d("trip_", "java - loop "+i+" , "+data.get(i)); } } }

코틀린 변환

override fun onClick(v: View?) { when(v?.id) { R.id.iv -> { JavaExample.setDataLoop() setDataLoop() } } } private val data = ArrayList<String>() private fun setDataLoop() { data.clear() data.add("trip") data.add("store") data.add("extriber") for (i in data.indices) { Log.i("trip_", "kotlin - loop " + i + " , " + data[i]) } }

뭐 비슷하게 변환이 되었고, 크게 바뀌는 부분은 없어요.

결과화면은 아래 이미지




별거 없어보여서 데이터 가져오는 부분을 한번 추가, 테스트해 봤어요

public static String getData(int index) {
        if(data != null) {
            if(data.size() > 0) {
                return data.get(index);
            }
        }
        return null;
    }

코틀린 변환

override fun onClick(v: View?) { when(v?.id) { R.id.iv -> { JavaExample.setDataLoop() setDataLoop() Log.d("trip_", "java - getData : " + JavaExample.getData(1)) Log.i("trip_", "kotlin - getData : " + getData(1)) } } } private fun getData(index: Int): String? { if (data!!.size > 0) { return data!![index] } return null }


결과화면 이미지







android studio ver. 3.1.2



아래의 이미지처럼 오른탭에 Java 로 먼저 구현하고,
(메뉴) Code > Convert Java File Kotlin File 를 선택하면 코틀린으로 변환이 되지만,

절대 프로젝트 전체를 이런식으로 변화해서 쓰시면 안됩니다. 정식버전이긴 하지만 아직 오류도 있을 수 있고,

코틀린에 아직 능숙하지 않다면 전체 변환은 비추입니다.






1. Method 생성 ( 간단하게 두 인자를 받아서 String 으로 return 해 주었습니다.)

public static String setData(int num, String name) {
        return num + " , "+ name;
    }

코틀린 변환

private fun setData(num: Int, name: String): String {
        return num.toString() + " , " + name
    }

결과


여기서 Int 만 반환하고 싶다 라고 하면 아래와 같이 응용이 가능합니다.

private fun setData(num: Int, name: String): /*String*/Int {
        //return num.toString() + " , " + name
        return num
    }


2. if 예제 (스트링 비교, int 비교, boolean 비교)

public static void setCheck(String name, int num, boolean check) {
        if(name.equalsIgnoreCase("test")) {
            Log.d("trip_", "java - setCheck : in if");
        } else {
            Log.d("trip_", "java - setCheck : in else");
        }

        if(num == 1) {
            Log.d("trip_", "java - num == 1");
        } else {
            Log.d("trip_", "java - num != 1");
        }

        if(check) {
            Log.d("trip_", "java - check true");
        } else {
            Log.d("trip_", "java - check false");
        }
    }

코틀린 변환

private fun setCheck(name: String, num: Int, check: Boolean) {
        if (name.equals("test", ignoreCase = true)) {
            Log.i("trip_", "kotlin - setCheck : in if")
        } else {
            Log.i("trip_", "kotlin - setCheck : in else")
        }

        if (num == 1) {
            Log.i("trip_", "kotlin - num == 1")
        } else {
            Log.i("trip_", "kotlin - num != 1")
        }

        if (check) {
            Log.i("trip_", "kotlin - check true")
        } else {
            Log.i("trip_", "kotlin - check false")
        }
    }

결과



Null 을 받아서 처리를 해야할 경우도 생길수가 있는데 해당 부분은 
아래와 같이 ? 만 추가 해주면 가능합니다.


private fun setCheck(name: String?, num: Int, check: Boolean) {






android studio ver. 3.1.2


많은  안드로이드 유명 앱서비스들이 java 에서 코틀린으로 넘어가고 있다고 하므로,

개발은 트랜드기 때문에 따라가야하는 것 아니겠습니까

이번에는 findViewById 및 onClickListener 예제를 살펴보겠습니다.



먼저 SplashActivity.java 를 만들어서 자바 코딩으로 Imageview를 불러와서 onClick 의 로그를 찍어 보았습니다.

당연한 이야기 이겠지만, SplashActivity.java 와 SplashActivity.kt 가 각각 만들어진다고 해도, 빌드시에는 같은 클래스로 인식하므로
_java 라고 따로 이름을 다르게 지정하였습니다.


그럼 다음과 같이 코드를 컨버팅 해보았습니다. 약간 문법이 달라진 것 외에는 비스무리하게 생겨서

크게 이질감도 들지 않고, 조금의 공부를 하면 능숙하게 잘 다룰수 있을 것 같네요


class SplashActivity : AppCompatActivity(), View.OnClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        val v = findViewById(R.id.iv)
        v.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v?.id) {
            R.id.iv -> {
                Log.d("trip_", "iv click")
            }
        }
    }
}


맨 위의 이미지 처럼 log도 onclick 도 제대로 되는군요!





android studio ver. 3.1.2


기존 java 로 된 프로젝트를 바꾸기 위해서는 , 


먼저 코틀린 관련 lib 가 어떻게 적용되어 있나 확인해 보았습니다.

android studio 로 새프로젝트를 만들게 되면,

이젠 java가 아닌 .kt 확장자를 가진 새로운 클래스 파일로 만들어 진 것을 확인 할 수 있어요.



먼저 프로젝트 build.gradle 에 gradle 버전과 함께 무엇인가가 추가 되어 있네요?
line 4, 코틀린 버전
line 14, 코틀린 관련 dependencies

그럼 기존 프로젝트에선 2가지만 추가해주면 될 듯 합니다.


buildscript {
    ext.kotlin_version = '1.2.30'
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url 'https://maven.google.com' }
        maven { url 'https://maven.fabric.io/public' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

app의 모듈 build.gradle 에는 
두가지 플러그인을 적용하였고, 라이브러리를 적용되어 있습니다.


기존의 프로젝트에 이렇게만 추가해서 코틀린 코드들을 적용할 수 있겠어요


apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    ...
}

dependencies {

    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

}


+ Recent posts