Kotlin 생성자

|

constructor 키워드

기본적으로 코틀린에서 클래스 생성자는 다음과 같이 클래스 선언부에서 constructor 키워드를 시용해서 만들어줍니다.

class Person constructor(name: String, age: Int)

위와 같이 클래스 선언부에서 생성자를 사용할 때는 constructor 키워드를 생략할 수 있습니다.

class Person(name: String, age: Int)


생성자를 여러 개 선언할 경우

class Person() {
    constructor(name: String, age: Int) : this()

    constructor(name: String) : this()
}
class Person(name: String) {
    constructor(name: String, age: Int) : this(name)
}

와 같은 형태로 사용할 수 있습니다. 생성자 뒤에 this 함수 호출하는 부분은 필수입니다.


생성자 매개 변수의 기본값 설정

class Person(name: String = "default", age: Int = 0)


생성자 함수 바디

클래스에서 변수의 값 선언 외에 별도 처리가 필요한 경우는 init {} 함수를 사용해서 처리할 수 있습니다.

class Person(var name: String?, val age: Int = 0) {
    init {
        if (name.isNullOrEmpty()) {
            name = "snowdeer"
        }
    }
}

생성자 매개 변수에서 val, var 등의 선언이 생략되면 기본적으로는 val로 인식이 됩니다.


접근 제한자

  • private: 클래스 내부에서만 접근 가능
  • protected: 상속받은 클래스에서만 접근 가능
  • internal: 같은 모듈 안에서만 접근 가능
  • 생략: 생략된경우 public로 간주됨

Protobuf 사용하는 예제

|

Android에서 Protobuf를 사용하는 방법입니다.


build.gradle(프로젝트)

먼저 프로젝트의 build.gradle에 다음과 같은 세팅을 합니다.

buildscript {
    ext.protobufVersion = '0.8.6'

    dependencies {
        classpath "com.google.protobuf:protobuf-gradle-plugin:$protobufVersion"
    }
}


build.gradle(모듈)

먼저 플러그인(plugin) 선언을 합니다.

apply plugin: 'com.google.protobuf'

그리고 build.gradle 파일 가장 아래쪽에 다음과 같은 코드를 입력합니다.

protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.0.0'
    }
    plugins {
        javalite {
            artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
        }
    }
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                remove java
            }
            task.plugins {
                javalite {}
            }
        }
    }
}

그리고 아래 항목도 추가합니다.

dependencies {
    implementation 'com.google.protobuf:protobuf-lite:3.0.0'
}


proto 파일 정의

src/main 아래로 가보면 기본적으로 java, res 라는 디렉토리가 존재합니다. 여기에 proto라는 디렉토리를 하나 만들어줍니다. 그리고 샘플 proto 파일을 하나 만듭니다. 저는 snowdeer_sample.proto라는 이름으로 만들었습니다.

Android Studio의 ProjectView에서 보기 모드가 Android로 되어 있으면 해당 위치가 제대로 보이지 않기 때문에 Project 등으로 변경해줍시다.

snowdeer_sample.proto

syntax = "proto2";

package com.snowdeer.sample;

option java_package = "com.snowdeer.sample.proto";
option java_outer_classname = "SampleProto";

message User {
  required int32 id = 1;
  required string name = 2;
  optional string address = 3;

  enum PhoneType {
    UNKNOWN = 0;
    MOBILE = 1;
    HOME = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = UNKNOWN];
  }

  repeated PhoneNumber phoneNumbers = 4;
}

message UserList {
  repeated User users = 1;
}


빌드

이제 모듈 빌드를 해봅니다. 빌드하고 나면 build/generated/source/... 디렉토리 아래에 위에서 정의한 이름(예제에서는 SampleProto)의 Java 파일이 생성된 것을 확인할 수 있습니다.

이제 Activity에서는 다음과 같이 사용을 해볼 수 있습니다.

package com.snowdeer.protobufexample;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.snowdeer.sample.proto.SampleProto.User;
import com.snowdeer.sample.proto.SampleProto.User.PhoneNumber;
import com.snowdeer.sample.proto.SampleProto.User.PhoneType;

public class MainActivity extends AppCompatActivity {

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

    PhoneNumber pn = PhoneNumber.newBuilder()
        .setType(PhoneType.MOBILE)
        .setNumber("010-1111-2222")
        .build();

    User user = User.newBuilder()
        .setId(100)
        .setName("snowdeer")
        .setAddress("Seoul")
        .addPhoneNumbers(pn)
        .build();

    Log.i("snowdeer", "[snowdeer] user: " + user.toString());
  }
}


실행 결과

   address: "Seoul"
    id: 100
    name: "snowdeer"
    phone_numbers {
      number: "010-1111-2222"
      type: MOBILE
    }

의존 라이브러리의 버전을 항상 최신으로 사용하도록 하는 gradle 옵션

|

기본적으로 의존 라이브러리를 사용하기 위해서는 build.gradle안에 다음과 같이 선언합니다.

dependencies {
    implementation group: 'com.android.support', name: 'appcompat-v7', version: '28.0.0'
}

위와 같이 사용하거나 짧게 줄여서 group:name:version 형태로 사용할 수도 있습니다.

dependencies {
    implementation 'com.android.support:appcompat-v7:28.0.0'
}

만약 항상 최신 버전의 라이브러리를 사용하고 싶을 때는 version 값에 latest.integration을 넣으면 됩니다.

클래스명, 함수명까지 자동으로 출력해주는 Log class

|

TimeUtil.java

import android.icu.util.Calendar;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class TimeUtil {

  public synchronized static long getTimeAsLong() {
    Calendar calendar = Calendar.getInstance();
    return calendar.getTimeInMillis();
  }

  public synchronized static String getTimeAsString(String format) {
    Date date = new Date(getTimeAsLong());
    SimpleDateFormat sdf = new SimpleDateFormat(format,
        Locale.getDefault());

    return sdf.format(date);
  }

  public synchronized static Long getTimeAsLong(String format, String text) {
    try {
      SimpleDateFormat sdf = new SimpleDateFormat(format,
          Locale.getDefault());
      Date date = sdf.parse(text);
      return date.getTime();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return Long.valueOf(-1);
  }

  public synchronized static String getTimeAsString(String format, long time) {
    Date date = new Date(time);
    SimpleDateFormat sdf = new SimpleDateFormat(format,
        Locale.getDefault());

    return sdf.format(date);
  }
}


Log.java

public class Log {

  private static final String TAG = "ActionManager";
  private static final String PREFIX = "[ram]";

  public synchronized static void v(String message) {
    android.util.Log.v(TAG, getDecoratedLog(message));
  }

  public synchronized static void d(String message) {
    android.util.Log.d(TAG, getDecoratedLog(message));
  }

  public synchronized static void i(String message) {
    android.util.Log.i(TAG, getDecoratedLog(message));
  }

  public synchronized static void w(String message) {
    android.util.Log.w(TAG, getDecoratedLog(message));
  }

  public synchronized static void e(String message) {
    android.util.Log.e(TAG, getDecoratedLog(message));
  }

  private static String getDecoratedLog(String message) {
    StackTraceElement ste = Thread.currentThread().getStackTrace()[4];

    StringBuilder sb = new StringBuilder();
    sb.append(PREFIX);
    sb.append(" [");
    sb.append(TimeUtil.getTimeAsLong());
    sb.append(" ");
    sb.append(ste.getFileName().replace(".java", ""));
    sb.append("::");
    sb.append(ste.getMethodName());
    sb.append("] ");
    sb.append(message);
    return sb.toString();
  }
}

만약 Line Number까지 출력하고 싶으면 ste 변수의 getLineNumber() 메소드를 사용하면 됩니다.

try-finally 보다는 try-with-resources를 사용하자.

|

예외 처리를 위해 사용하는 try - finally 구문은 강력하긴 하지만, try가 2개 이상 사용되거나 그 외의 이유 등으로 코드가 복잡하게 될 수 있는 단점이 있습니다. Java 7.0 부터 지원하는 try with resources를 사용하면 코드를 훨씬 깔끔하게 구현할 수 있습니다.

try-finally 예시

public class FileUtil {

  public static String getFirstLineOfFile(String filepath) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(filepath));
    try {
      return br.readLine();
    } finally {
      br.close();
    }
  }
}


2개 이상의 try가 사용될 경우

public class FileUtil {

  private static final int BUFFER_SIZE = 1024;

  public static void copy(String src, String dest) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
      OutputStream out = new FileOutputStream(dest);
      try {
        byte[] buf = new byte[BUFFER_SIZE];
        int n;
        while ((n = in.read(buf)) >= 0) {
          out.write(buf, 0, n);
        }
      } finally {
        out.close();
      }
    } finally {
      in.close();
    }
  }
}


try-with-resources 예제

public class FileUtil {

  private static final int BUFFER_SIZE = 1024;

  public static String getFirstLineOfFile(String filepath) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
      return br.readLine();
    }
  }

  public static void copy(String src, String dest) throws IOException {
    try (InputStream in = new FileInputStream(src);
        OutputStream out = new FileOutputStream(dest);) {
      byte[] buf = new byte[BUFFER_SIZE];
      int n;
      while ((n = in.read(buf)) >= 0) {
        out.write(buf, 0, n);
      }
    }
  }
}


catch 사용 예제

public static String getFirstLineOfFile(String filepath) {
  try (BufferedReader br = new BufferedReader(new FileReader(filepath))) {
    return br.readLine();
  }
  catch (IOException e) {
    e.printStackTrace();
    return "";
  }
}