코틀린은 자바와 호환이 되기 때문에, 자바로 쓴 생성자도 가져다 쓸 수 있다.
fun main() {
val p = Person(10, "123") //java로 Person 정의한 거 사용
p.Say()
}
문자열 사용이 편리하고, 변수 타입은 뒤에 쓴다.
fun main() {
val number : Int= 10 //Int 타입
var message = "Hello JAVA" //var : 가변 변수 | val : 불변 변수
println("number: $number") //문자열 내 변수 바로 사용 가능
println("message: $message")
}
삼항 연산자 대신 if문으로 바로 넣을 수 있다.
Switch문 대신 when을 쓴다.
fun main() {
val number : Int= 10
var message = if(number%2==0) "Even" else "Odd"
println("message: $message")
when{
number % 2 == 0 -> println("Even")
number % 2 != 0 -> println("Odd")
}
//변수에 값 할당에 when 쓰면 else가 필요하다.
}
반복문은 for와 for-in이 있다.
fun main() {
for(i in 0 until 5) { //until은 포함하지 않는다. (5 출력X)
println(i)
}
for(i in 0 .. 4) { // ...은 포함한다. (4까지 출력)
println(i)
}
}
함수도 다양하게 쓸 수 있다.
fun main() {
val result = add(1,2) //add 함수 덕에 타입 추론이 됨
println(result)
}
fun add(a:Int, b:Int): Int{
return a+b
}
fun add1(a:Int, b:Int)=return a+b; //이렇게도 가능
객체 생성은 생성자가 없어도 됨
fun main() {
val p = Person( "123")
p.say()
}
class Person(private val name:String){ //변수는 여기에 (생성자는 필요 없음)
fun say(){ //기본이 public
println("$name is saying")
}
}
list도 가변, 불변이 있다. (불변 권장)
fun main() {
val num1=listOf("1","2","3") //불변리스트
val num2=mutableListOf("1","2","3") //가변리스트 (비추)
for(i in num2){
println(i)
}
}
map도 가변, 불변 있음. (역시나 불변 권장)
fun main() {
val num1 = mapOf("1" to 1, "2" to 2, "3" to 3) //불변
val num2 = mutableMapOf("1" to 1, "2" to 2, "3" to 3) //가변
num2.put("4", 4)
for ((key, value) in num2) { //튜플 형태로 이용
println("$key is $value")
}
}
Null Safety가 있어 null 처리에 유리하다.
fun main() {
val name: String? =null // ?로 null 허용
// ?로 name가 null인지 확인하고, ?: 을 사용하여 null이면 다른 걸로 처리 가능
println(name?.length ?: "Name is Null")
}
+) ?: : 엘비스 연산자
+) ? 썼는데 null이면 그 뒤에 건 실행을 안한다.
람다 표현식도 축약 가능
fun main() {
val num1=listOf("1","2","3")
num1.forEach {n->println("$n")}
num1.forEach {println(it)} //매개변수가 1개면 생략 가능 (it 고정)
}
데이터 class로 equals(), hashCode(), toString() 자동 생성 가능
fun main() {
val p1 = Person("123")
val p2 = Person("456")
println(p1) // toString() 자동 생성된 거임
println(p1 == p2) // equals() 도 자동 생성
}
data class Person(val name:String){}
확장 함수? -> 기존 클래스에 새로운 함수 추가하는 기능 (유틸 클래스 추가 없이)
fun main() {
val name:String="name"
name.aaa()
}
fun String.aaa() { //확장 함수
println("$this ~!!")
}
오버로딩 없이 설정하는 기본값
fun main() {
say() //"기본값" 출력
say("new Name") //"new Name" 출력
}
fun say(name:String = "기본값"){
println(name)
}
object로 빠르게 객체 싱글톤 만들기
object Singleton {
fun show() {
println("Hello")
}
}
class Test {
}
fun main() {
val a = Singleton //()없어도 됨
val b = Singleton
println(a == b) // Singleton 이므로 항상 같은 인스턴스가 반환됨.
val t1 = Test()
val t2 = Test()
println(t1 == t2) // 객체 생성마다 새로운 인스턴스가 생성되므로 서로 다른 인스턴스임.
}
+) JAVA로 싱글톤 만들기
public class Singleton {
//최초 생성
private static final Singleton INSTANCE = new Singleton();
private Singleton() {} //외부 접근 차단
//최초 생성한 거 줌
public static Singleton getInstance() {
return INSTANCE;
}
public void show() {
System.out.println("Hello");
}
public static void main(String[] args) {
Singleton.getInstance().show();
}
}
예외처리는 try를 표현식으로 사용 가능
fun main() {
//try-catch를 강제하지 않기 때문에 그냥 쓰면 된다.
//throw도 필요 없음
Files.copy(File("a.txt").toPath(), File("a_copy.txt").toPath(), StandardCopyOption.REPLACE_EXISTING)
}
filter로 리스트 필터링 (java 스트림과 유사)
fun main() {
val num = listOf(1,2,3,4,5,6,7,8,9)
val newNum= num.filter { it % 2 == 0 } //배개변수 1개라 it으로 받음
for(i in newNum){
println(i)
}
}
forEach와 map 기본 제공
fun main() {
val num = listOf(1,2,3,4,5,6,7,8,9)
//map로 각 요소에 10씩 추가
//forEach로 각 요소 출력
num.map{it+10}.forEach {println(it)}
}
is로 타입 캐스팅 (체크와 캐스팅을 한번에)
fun main() {
val obj: Any = "hi"
if(obj is String) {
println(obj.length)
}
}
스코프 함수 (apply, let, also, run, with)
-> 특정 객체(컨텍스트)의 범위를 지정하여 해당 객체의 멤버(프로퍼티, 메소드)에 간결하고 효율적으로 접근할 수 있게 해주는 코틀린의 기능
| 함수 | 리턴값 | 컨텍스트(this or it) | 주요 사용 목적 |
|---|---|---|---|
| apply | 객체 자체 | this | 객체 설정 (초기화, 프로퍼티 변경) |
| let | 람다 결과값 | it | 안전 호출과 변환 (Nullable 처리, 새로운 값 반환) |
| also | 객체 자체 | it | 객체 유지 + 추가 로직 수행 (디버깅, 로깅) |
| run | 람다 결과값 | this | 객체 설정 + 결과 반환 (객체 없이 블록 실행도 가능) |
| with | 람다 결과값 | this | 객체를 컨텍스트로 작업 수행 후 결과 반환 |
fun main() {
//apply (객체를 생성 -> 내부 속성 설정 -> 객체 반환)
val p = Person().apply {
//this(객체)로 넘어와서 객체가 반환
this.name="new Name"
this.age = 20
}
p.printInfo() //Name: new Name , Age: 20
//let (null이 아닌 경우 특정 로직을 실행)
val rst1 = p.name?.let {
//it으로 넘어옴 (name 길이: 8)
println("name 길이: ${it.length}") //name 길이: 8
it.length //rst에 10 할당
}
//also 객체를 유지하며 추가적 작업 수행
p.age.also { println(it) } //20
println("rst1 : $rst1") //rst1 : 8
//run 객체 받아 작업 수행 후 결과 리턴
val rst2 = p.run {
this.age * 2 + 10
}
println("rst2 : $rst2") //rst2 : 50
//with 객체를 받아 작업 수행 후 결과 리턴
with(p) {
increaseAge(10) //this.increaseAge()와 동일
printInfo()
}
}
class Person {
var name: String?=""
var age: Int=0
fun printInfo(){
println("Name: $name , Age: $age")
}
fun increaseAge(years: Int) {
age += years
}
}
lateinit으로 나중에 초기화
fun main() {
var p = Person()
p.name = "new Name"
println(p.name)
}
class Person {
lateinit var name: String
}
+)주의!! lateinit 하면 컴파일러가 잡아주지 않는다...
lasy로 최초 접근시 초기화 하기
fun main() {
val name : String by lazy {
println("initializing")
"Hello"
}
println(name)
}
동반 객체 (companion object) : 정적 멤버 선언용. 객체처럼 쓸 수 있는 static
fun main() {
println(MathUtil.PI)
println(MathUtil.sq(3))
}
class MathUtil{
companion object{
val PI=3.14
fun sq(n:Int)=n*n
}
}
open 키워드로 상속 받을 수 있게 하기
open class 클래스명 형식으로 적어야 이걸 상속 받는 클래스를 만들 수 있다.
(기본적으로는 java의 final 형태)
fun main() {
val aa = AA()
aa.fun1() // AA 출력
}
open class A { //상속 가능하게 open 붙여줌
open fun fun1() { //메서드도 open 붙여줌
println("A")
}
}
//:로 오버라이딩, 인터페이스 정의하기 (둘다 :로 쓴다.)
//다중 구현은 , 하고 하면 됨 (AA: A,B 같이)
class AA : AA() {
// 메서드 오버라이딩 가능함. (override 붙여야함)
override fun fun1() {
println("AA")
}
}
+) 다중 구현 : 상속은 java, kotlin 모두 1개 밖에 못 받지만, 인터페이스는 둘다 여러개를 상속받는 다중 상속을 지원함
필드 수정은 막고, 접근은 허용할 때는 val (굳이 private 없어도 됨)
반대로 수정을 허가하고, 접근 불가일 때는 Getter 호출 시 예외 발생하게 하면 됨
fun main() {
val p = Person()
p.name = "KIM"
println(p.name)
}
class Person {
//그냥 var로 하면 getter, setter가 자동으로 생성.
private var _name: String = "AAA"
//getter, setter 직접 구현 (가상 필드)
var name: String
get() = throw IllegalAccessException("Can't access") //예외처리
set(value) {_name = value} //setter는 허용
}
생성자와 init 블록
init 블록: 첫 생성시 값을 세팅하면서 로직 실행
fun main() {
val a = A("abc", 25)
}
class A(val a: String, val b: Int) { //주생성자
//부생성자 (this는 주 생성자 호출하는 거) (=부생성자로 주생성자 만들기임)
constructor(a: String) : this(a, 0) //a값만 받고, b 값은 그냥 0으로 하는거
//init 블록
init {
println("객체 생성되었습니다: $a, $b")
}
}
+) 사실 부생성자는 그냥 val a: String, val b: Int =0 해도 되긴 한다. (a 값만 받고, b는 기본 0)