자바에서 코틀린 프로퍼티를 호출할때 Getter, Setter를 사용합니다.
class Student {
var name: String? = null
var birthDate: LocalDate? = null
}
public class JavaGetterSetterExample {
public static void main(String[] args) {
Student student = new Student();
student.setName("홍길동");
student.setBirthDate(LocalDate.of(1918, 7, 4));
System.out.println(student.getName()); //홍길동
System.out.println(student.getBirthDate()); //1918-07-04
}
}
val로 선언한 프로퍼티는 불변이기 때문에 Setter가 존재할 수 없고 Getter만 존재할 수 있습니다.
class Student {
var name: String? = null
var birthDate: LocalDate? = null
val age: Int = 10
}
public static void main(String[] args) {
student.setAge(10); // 컴파일 에러
System.out.println(student.getAge());
}
Setter를 private 가시성으로 변경하면 var로 선언해도 Setter를 없앨 수 있습니다.
class Student {
var name: String? = null
var birthDate: LocalDate? = null
val age: Int = 10
var grade: String? = null
private set
}
private set으로 세터를 제거하면 내부에서만 변경하거나 외부 접근 함수를 별도로 만들어야합니다.
class Student {
var grade: String? = null
private set
fun changeGrade(grade:String) {
this.grade = grade
}
}
코틀린의 프로퍼티는 자바로 변환시 필드는 private이고 Getter, Setter를 통해서만 접근이 가능합니다.
@JvmField를 사용하면 Getter, Setter를 쓰지 않고 자바 프로퍼티로 사용할 수 있습니다.
class Student {
@JvmField
var name: String? = null
}
public static void main(String[] args) {
Student student = new Student();
student.setName("홍길동"); // 컴파일 에러
System.out.println(student.getName()); // 컴파일 에러
student.name = "홍길동"; // 정상 실행
System.out.println(student.name); // 정상 실행, '홍길동' 출력
}
fun String.first(): Char {
return this[0]
}
fun String.addFirst(char: Char): String {
return char + this.substring(0)
}
fun main() {
println("ABCD".first()) // 출력 : A
println("ABCD".addFirst('Z')) // 출력 : ZABCD
}
public class ExtensionExample {
public static void main(String[] args) {
"ABCD".first(); // 컴파일 에러
}
}
public final class MyExtensionsKt {
public static final char first(@NotNull String $this$first) {
Intrinsics.checkNotNullParameter($this$first, "$this$first");
return $this$first.charAt(0);
}
@NotNull
public static final String addFirst(@NotNull String $this$addFirst, char var1) {
Intrinsics.checkNotNullParameter($this$addFirst, "$this$addFirst");
byte var4 = 0;
String var5 = $this$addFirst.substring(var4);
Intrinsics.checkNotNullExpressionValue(var5, "this as java.lang.String).substring(startIndex)");
return var1 + var5;
}
}
즉 자바에서 사용할땐 파일명(클래스명).메서드명 형태로 사용해야합니다.
ex) MyExtensionsKt.first(”ABCD”);
public static void main(String[] args) {
//"ABCD".first();와 동일!!
MyExtensionsKt.first("ABCD");
//"ABCD".addFirst();와 동일!!
MyExtensionsKt.addFirst("ABCD", 'Z');
}
롬복은 자바 진영에서 가장 많이 사용되는 라이브러리 중 하나로 애너테이션을 통해 생성자, 게터, 세터, 빌더 등을 자동으로 생성해줍니다.
@EqualsAndHashCode
@ToString
public class Hero {
@Getter
@Setter
private String name;
private int age = 53;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
Hero hero = new Hero();
hero.setName("아이언맨");
hero.setAddress("스타크타워");
// 자바에선이와 같은 방식으로 사용!
}
}
그러나 코틀린과 자바를 상호 운용하는 경우 롬복을 사용하면 정상 동작하지 않습니다.
fun main() {
val hero = Hero()
hero.name = "아이언맨"
println(hero.name)
}
// 실행시 컴파일 에러 발생
fun main() {
val hero = Hero()
hero.address = "스타크타워"
println(hero.address)
// 스타크타워
}
코틀린과 자바 통합 프로젝트에서 컴파일되는 과정
롬복을 제거하고 IDE의 생성 기능을 이용합니다.
public class Hero {
private String name;
private int age;
private String address;
public String getAddress() {
Chapter 05.자바 프로젝트에 코틀린 도입해보기 - 05.코틀린과 롬복 4
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hero hero = (Hero) o;
return age == hero.age && Objects.equals(name, hero.name) && Objects.equals(address, hero.address);
}
@Override
public int hashCode() {
return Objects.hash(name, age, address);
}
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
롬복이 적용된 클래스는 대부분 데이터 관련 클래스인 경우가 많습니다.
코틀린의 데이터 클래스로 마이그레이션하는 것을 추천합니다.
자바 프로젝트를 점진적으로 코틀린으로 전환하기 위해 이 방법이 가장 좋다고 생각합니다.
이 외에도 코틀린에서 제공하는 롬복 플러그인을 사용할 수 있습니다.
data class HeroKt(
val name: String,
val age: Int,
val address: String,
)
fun main() {
val heroKt = HeroKt(name = "아이언맨", age = 50, address = "스타크타워")
println(heroKt)
}
아래 코드는 컴파일 에러가 발생하므로 open 키워드를 붙여야합니다.
@SpringBootApplication
// class KotlinJavaSpringApplication // 컴파일 에러
open class KotlinJavaSpringApplication
fun main(args: Array<String>) {
runApplication<KotlinJavaSpringApplication>()
}
매번 open 키워드를 붙이는 건 불편하므로 코틀린은 All-open 컴파일러 플러그인을 제공합니다.
plugins {
id ("org.springframework.boot") version "2.7.0"
id ("io.spring.dependency-management") version "1.0.11.RELEASE"
id ("org.jetbrains.kotlin.jvm") version "1.6.21"
id ("org.jetbrains.kotlin.plugin.allopen") version "1.6.21" // 추가
}
// 추가
allOpen {
annotations("org.springframework.boot.autoconfigure.SpringBootApplication")
}
@Transactional도 open을 붙이지 않으면 컴파일 에러가 발생하며 플러그인에 추가해야합니다.
allOpen {
annotations(
"org.springframework.boot.autoconfigure.SpringBootApplication",
"org.springframework.transaction.annotation.Transactional"
)
}
이처럼 매번 문제가 생길때마다 allopen에 추가하기 번거롭고 어려우므로 allopen 플러그인을 래핑한 kotlin-spring 플러그인을 사용하면 매우 간편해집니다.
plugins {
id("org.springframework.boot") version "2.7.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.jetbrains.kotlin.jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
}
kotlin-spring 플러그인은 스프링에서 CGLIB 프록시를 사용하는 모든 애노테이션에 대해 자동으로 open 처리를 해줍니다.
실제로 예시와 같이 엔티티를 작성하면 컴파일 에러가 발생합니다.
@Entity
@Table
class Person(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long?,
@Column
var name: String,
@Column
var age: Int,
)
코틀린은 매개 변수가 없는 기본 생성자를 자동으로 만들어주는 no-arg 컴파일러 플러그인을 제공합니다.
plugins {
id("org.springframework.boot") version "2.7.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.jetbrains.kotlin.jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.noarg") version "1.6.21"
}
noArg {
annotation("javax.persistence.Entity")
}
또한 JPA를 쓸 경우 Spring Plugin과 마찬가지로 kotlin-jpa 플러그인을 제공합니다.
JPA 플러그인을 쓰면 @Entity, @Embeddable, @MappedSuperclass에 대한 기본 생성자를 자동으로 생성해줍니다.
plugins {
id("org.springframework.boot") version "2.7.0"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
id("org.jetbrains.kotlin.jvm") version "1.6.21"
kotlin("plugin.spring") version "1.6.21"
kotlin("plugin.jpa") version "1.6.21"
}
출처 : fastcampus