fun main(){
val a1: KClass<String> = String::class
println("String의 코틀린에서의 타입 : $a1")
val a2:Class<String> = String::class.java
println("String의 자바에서의 타입 : $a2")
// 변수를 통해 접근할 수 있는 객체의 클래스 타입을 파악한다.
val str1 = "안녕하세요"
val a3:KClass<out String> = str1::class
println("str1을 통해 접근할 수 있는 객체의 클래스 타입(코틀린) : $a3")
val a4:Class<out String> = str1::class.java
println("str1을 통해 접근할 수 있는 객체의 클래스 타입(자바) : $a4 ")
val test1 = TestClass()
val a7 = test1::class
val a8 = test1::class.java
println("test1의 클래스 타입(코틀린) : $a7")
println("test1의 클래스 타입(자바) : $a8")
// 클래스 정보 분석
println("추상 클래스 인가 : ${test1::class.isAbstract}")
println("Companion 클래스 인가 : ${test1::class.isCompanion}")
println("Data 클래스 인가 : ${test1::class.isData}")
println("final 클래스 인가 : ${test1::class.isFinal}")
println("open 클래스 인가 : ${test1::class.isOpen}")
println("중첩 클래스 인가 : ${test1::class.isInner}")
println("Sealed 클래스 인가 : ${test1::class.isSealed}")
}
프로그램 실행 중 객체에 대한 다양한 정보를 파악하기 위한 개념으로 클래스, 인터페이스, 메서드, 프로퍼티 등의 정보를 알아 낼 수 있다.
:: 연산자
를 사용하여 참조하는 위치와 메서드 또는 매개변수 형식 및 반환 형식에 따라 참조하는 타입이 결정된다.
KClass<클래스타입>
: 지정된 클래스의 타입을 파악한다(코틀린 클래스)
Class<클래스타입>
: 지정된 클래스의 타입을 파악한다(자바 클래스)
val lambda1 : (Int, Int) -> Int = {a1:Int, a2:Int -> a1 + a2}
// 타입 부분을 생략할 수 있다.
val lambda2 = {a1:Int, a2:Int -> a1 + a2}
// 뒷 부분의 타입을 생략할 수 있다.
val lambda3 : (Int, Int) -> Int = {a1, a2 -> a1 + a2}
// 제일 마지막에 작성한 값이 반환 값이 된다.
val lambda4 = {a1:Int, a2:Int ->
val r1 = a1 + a2
var r2 = a1 - a2
r1 * r2
}
람다식
을 작성하여 변수에 담아두면 그 것을 이용해람다식
을 계산하고 그 결과를 받아 사용할 수 있다.
// 익명함수
val test3 = fun(){
println("나는야 익명함수")
}
변수에 이름없는 함수로 함수를 정의하면서 동시에 사용할 수 있는 함수이다.
fun main(){
testFunction1()
testFunction1()
testFunction2()
testFunction2()
}
fun testFunction1(){
println("----------------------------")
println("testFunction1")
println("----------------------------")
}
inline fun testFunction2(){
println("----------------------------")
println("testFunction2")
println("----------------------------")
}
public final class MainKt {
public static final void main() {
testFunction1();
testFunction1();
int $i$f$testFunction2 = false;
String var1 = "----------------------------";
System.out.println(var1);
var1 = "testFunction2";
System.out.println(var1);
var1 = "----------------------------";
System.out.println(var1);
$i$f$testFunction2 = false;
var1 = "----------------------------";
System.out.println(var1);
var1 = "testFunction2";
System.out.println(var1);
var1 = "----------------------------";
System.out.println(var1);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
public static final void testFunction1() {
String var0 = "----------------------------";
System.out.println(var0);
var0 = "testFunction1";
System.out.println(var0);
var0 = "----------------------------";
System.out.println(var0);
}
public static final void testFunction2() {
int $i$f$testFunction2 = 0;
String var1 = "----------------------------";
System.out.println(var1);
var1 = "testFunction2";
System.out.println(var1);
var1 = "----------------------------";
System.out.println(var1);
}
}
자바 코드로 변경될 때
inline 함수
를 호출하는 부분들은 함수 내부의 코드로 변경된다. 코드가 다소 늘어나지만 함수 호출과 관련된 작업을 하지 않는 장점이 있다. 딱 한번만 사용하는 함수가 있을 때 사용하는 경우가 종종 있다.
fun main(){
val str1 = "abcd"
// 추가한 메서드 호출
println(str1.getUpperString())
str1.printString()
}
// 확장함수 정의
// 클래스명.추가할 메서드
fun String.getUpperString() : String {
return this.uppercase(Locale.getDefault())
}
fun String.printString() {
println("문자열 : $this")
}
이미 있는 클래스에 메서드를 추가하는 개념 추가된 메서드는 같은 프로그램 내에서만 사용이 가능하다. 자바 코드로 변경될 때 객체의 ID를 받아 사용하는 코드로 변경된다.
fun main(){
val v1 = 100
// 값(또는 객체).함수
val r1 = v1.add2(50)
println("r1 : $r1")
// infix가 있으므로 다음과 같이 호출 할 수도 있다.
var r2 = v1 add2 50
println("r2 : $r2")
val obj1 = TestClass1()
val obj2 = TestClass1()
obj1.number1 = 100
obj2.number1 = 200
val r3 = obj1 add4 obj2
println(r3.number1)
val r4 = obj1.add4(obj2)
println(r4.number1)
}
// infix fun 값1의 타입.함수이름(값2를 담을 매개변수) : 반환타입
infix fun Int.add2(a1:Int) : Int{
// 여기에서 this 첫 번째 값을 의미한다.
return this + a1
}
infix fun Int.minus2(a1:Int) : Int{
return this - a1
}
// 일반적인 함수는 infix로 정의할 수 없다.
// infix fun add3(a1:Int) : Int {
//
// }
// 클래스에 정의한 멤버 메서드
class TestClass1 {
var number1 = 0
infix fun add4(target:TestClass1) : TestClass1{
val r1 = this.number1 + target.number1
val t1 = TestClass1()
t1.number1 = r1
return t1
}
}
함수 호출을 연산자 사용하듯이 할 수 있는 함수이다. 값1 함수이름 값2 형태로 호출 한다. 값1 객체를 통해 함수를 호출하고 매개변수로 값2를 전달한다.
fun main(){
// 함수를 호출할 때 전달할 익명 함수
val t1 = fun(x1:Int, x2:Int) : Int {
return x1 + x2
}
// 고차함수 호출
testFunc1(100, 200, t1)
// 익명함수를 직접 작성해 준다.
testFunc1(100, 200, fun(x1:Int, x2:Int) : Int {
return x1 - x2
})
// 고차함수에 람다 함수를 전달하는 것도 가능하다.
val lambda1 = {x1:Int, x2:Int -> x1 * x2}
testFunc1(100, 200, lambda1)
// 람다식을 직접 작성해 준다.
testFunc1(100, 200, {x1:Int, x2:Int -> x1 / x2})
// 함수를 반환하는 함수 사용
val t2 = testFunc2(100)
val r2 = t2(200)
println("r2 : $r2")
// 람다식을 반환하는 함수 사용
val t3 = testFun3(100)
val r3 = t3(200)
println("r3 : $r3")
// 함수를 받는 매개변수가 마지막에 있지 않은 함수 호출
// 1. 익명함수를 변수에 담아 전달한다.
val f4 = fun(x1:Int, x2:Int) : Int{
println("x1 : $x1")
println("x2 : $x2")
return x1 + x2
}
testFunc4(100, f4, 200)
// 2. 익명함수를 직접 작성한다.
testFunc4(100, fun(x1:Int, x2:Int) : Int{
println("x1 : $x1")
println("x2 : $x2")
return x1 + x2
}, 200)
// 3. 람다를 전달한다.
val lambda4 = {x1:Int, x2:Int ->
println("x1 : $x1")
println("x2 : $x2")
x1 + x2
}
testFunc4(100, lambda4, 200)
// 4. 람다를 직접 작성한다.
testFunc4(100, {x1:Int, x2:Int ->
println("x1 : $x1")
println("x2 : $x2")
x1 + x2
}, 200)
// 익명함수나 람다식을 받는 매개변수가 마지막에 있는 함수 호출
// 위의 1 ~ 4 모두 사용가능하다.
// { } 로 작성한 람다식이 제일 마지막 매개변수로 들어간다.
testFunc5(100, 200) {x1:Int, x2:Int ->
println("x1 : $x1")
println("x2 : $x2")
x1 + x2
}
val ic1 = InterClass1()
testFunc6(ic1)
val ic2 = InterClass2()
testFunc6(ic2)
testFunc6(object:Inter1{
override fun interMethod() {
println("익명 중첩 클래스의 메서드 호출")
}
})
testFunc7 {
println("testFun7 1번")
}
testFunc7 {
println("testFun7 2번")
}
testFunc8 { a1:Int ->
println(a1)
}
testFunc8 {
println(it)
}
}
// 매개변수로 함수를 받는 함수
fun testFunc1(a1:Int, a2:Int, m1:(Int, Int) -> Int){
val r1 = m1(a1, a2)
println("testFun1 r1 : $r1")
}
// 함수를 반환하는 함수
fun testFunc2(a1:Int) : (Int) -> Int {
// 반환하는 함수 내부에서 지역변수나 매개변수를 사용할 수 있다.
return fun(x2:Int) : Int {
return a1 + x2
}
}
// 람다식을 반환하는 함수
fun testFun3(a1:Int) : (Int) -> Int {
return {x2:Int -> a1 - x2}
}
// 함수나 람다식을 받는 매개변수를 제일 마지막에 작성하지 않은 함수
fun testFunc4(a1:Int, m1:(Int, Int) -> Int, a2:Int){
val r4 = m1(a1, a2)
println("r4 : $r4")
}
// 함수나 람다식을 받는 매개변수를 제일 마지막에 작성한 함수
fun testFunc5(a1:Int, a2:Int, m1:(Int, Int) -> Int){
val r5 = m1(a1, a2)
println("r5 : $r5")
}
interface Inter1{
fun interMethod()
}
fun testFunc6(inter1:Inter1){
inter1.interMethod()
}
class InterClass1 : Inter1{
override fun interMethod() {
println("InterClass1의 interMethod")
}
}
class InterClass2 : Inter1{
override fun interMethod() {
println("InterClass2의 interMethod")
}
}
fun testFunc7(m1:()->Unit){
m1()
}
// 매개변수 하나를 가지고 있는 함수나 람다식을 받는 함수
fun testFunc8(m1:(Int) -> Unit){
m1(100)
}
함수를 매개변수로 받거나 반환 타입이 함수인 함수이다. 다른곳에서 호출하는 메서드를 오버라이딩하는 작업을 대신할 수 있다. 매개변수 하나인 함수나 람다식을 받는 고차함수를 사용할 때 전달할 함수나 람다식에 매개변수를 정의하지 않고
it
을 사용하면 매개변수로 전달되는 값을 사용할 수 있다.
val t7 = TestClass1(100, 200).let{
it.a3 = 300
it.a4 = 400
// t7 변수에 객체의 ID를 담기 위해 반환해줘야 한다.
it
}
println(t7)
객체에 접근할 때
it
변수를 사용해야 한다. 코드 블럭 마지막에 작성된 값이나 수식을 반환해준다. let 객체 생성 후 나중에 프로퍼티에 값을 저장하고자 할 때 사용한다. 만약 아래와 같이 객체 생성시 프로퍼티의 값을 저장하고 객체의 ID를 변수에 담겠다면 람다식 마지막에 객체의 ID(it을 통해)를 반환해야 한다.
val t8 = TestClass1(100, 200).apply{
a3 = 300
a4 = 400
}
println(t8)
생성된 객체의 ID가 apply 쪽으로 전달되어
this(혹은 생략)
을 통해 객체의 프로퍼티에 접근할 수 있다. { } 블럭 내부의 코드가 수행이 끝나면 생성된 객체의 ID가 변수에 담긴다. 이에 { } 블럭 내부에서 객체의 ID를 반환하는 코드를 작성하지 않는다.
val t9 = TestClass1(100, 200).run{
a3 = 300
a4 = 400
this
}
println(t9)
객체를 생성한 후에 run 코드 블럭 내로 객체가 전달되기 때문에
this(혹은 생략)
을 통해 프러퍼티에 접근할 수 있다. run { } 블럭의 수행이 끝나면 제일 마지막에 작성한 값이 변환되어 변수에 담긴다. 이에 생성된 객체의 ID를 전달하고자 한다면 run { } 블럭 제일 아래에 this를 작성하여 객체의 ID를 반환해야 한다.
val t10 = with(TestClass1(100, 200)) {
a3 = 300
a4 = 400
this
}
println(t10)
생성한 객체를 ( ) 안에 넣어줘야 한다. { } 내에는 with에 지정된 객체의 ID가 전달되기 때문에
this(혹은 생략)
로 프로퍼티에 접할 수 있다. 객체를 생성할 때 프로퍼티에 값을 설정하기 보단run
처럼 객체 생성한 이후 나중에 프로퍼티의 값을 새롭게 저장하기 위해 사용한다. run 보다 작성하는 코드가 더 많기 때문에 잘 사용하지는 않는다.
val t11 = TestClass1(100, 200).also {
it.a3 = 300
it.a4 = 400
}
println(t11)
객체를 생성과 함께 프로퍼티의 값을 설정하는 작업을 하면 객체의 ID가
it
이라는 변수로 전달되고 그 것을 통해 객체의 프로퍼티에 접근할 수 있다. 하지만 let과 다르게 생성된 객체의 ID가 반환되므로 마지막에 객체의 ID를 반환하지 않아도 된다.
객체를 생성하면서 바로 프로퍼티의 값을 설정하겠다면 -> apply
객체를 생성하고 나중에 프로퍼티의 값을 설정하겠다면 -> run