그 이유를 간략히 설명하자면, Effective Java에서는 다음과 같이 설명한다. equals를 재정의한 클래스에서는 hashcode도 재정의 해야한다.
그렇지 않으면 hash를 사용하는 HashMap, HashSet과 같은 컬렉션의 원소로 사용될 때 문제가 발생할 것이다.
따라서, eqauls를 재정의 하는 경우 다음 3가지 규약을 지켜야한다.
equals비교에 사용되는 정보가 변경되지 않았다면, 객체의 hashcode 메서드는 몇번을 호출해도 항상 일관된 값을 반환해야 한다.
(단, Application을 다시 실행한다면 값이 달라져도 상관없다. (메모리 소가 달라지기 때문))
equals메서드 통해 두 개의 객체가 같다고 판단했다면, 두 객체는 똑같은 hashcode 값을 반환해야 한다.
equals메서드가 두 개의 객체를 다르다고 판단했다 하더라도, 두 객체의 hashcode가 서로 다른 값을 가질 필요는 없다. (Hash Collision) 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.
age는 컴파일러에 의해 널 안정성 검사를 마친 후 값을 비교가 허용되므로, age의 프로퍼티값은래퍼 타입으로 저장된다.
2.2 숫자 변환
Kotlin은 Boolean을 제외한 모든 원시 타입에 대한 변환 함수를 제공
Kotlin은 한 타입의 숫자를 다른 타입으로 자동변환 하지 않음.
명시적으로 변환 메소드를 사용해야함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
val i = 1 val l = Long = i // "Error: type mismatch" 컴파일 오류 발생
val i = 1 val l: Long = i.toLong() // 명시적으로 변환 메소드 사용
---------------------------명시 이유--------------------------- val x = 1// Int val list = listOf(1L, 2L, 3L) // Long 리스트 x in list // 묵시적 타입 변환으로 인해 false
val x = 1 println(x.toLong() in listOf(1L, 2L, 3L)) //true
funfoo(l: Long) = pinrtln(l)
val b: Byte = 1// 상수 값는 적절한 타입으로 해석 된다. val l = b + 1L // +는 Byte와 Long을 인자로 받을 수 있다. foo(42) // 함수 인자이므로 42를 Long으로 해석한다.
2.3 Any, Any? : 최상위 타입
Java는 Object, Kotlin은 Any가 널이 될 수 없는 타입의 최상위타입
내부적으로 Any는 자바의 java.lang.Object로 컴파일 된다.
모든 코틀린 클래스에는 toString, equals, hashCode 라는 3개의 메소드가 있다. 하지만 java.lang.Object에 있는 다른 메소드(wait나 notify 등)는 Any에서 사용할 수 없다. 그런 메소드를 사용하고 싶다면 java.lang.Object로 캐스트해야 한다.
2.4 Unit 타입 : Kotlin의 void
Kotlin의 Unit타입은 Java의 void와 같은 기능을 함.
1 2
funf(): Unit{...} funf(){...} //fun의 기본 선언은 void라고 생각
Ex.) void 와Unit의 차이
반환 타입으로 Unit을 반환가능
타입 매개변수로 Unit을 쓸 수 있다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
interfaceProcessor<T> { funprocess(): T }
//반환 타입으로 Unit을 묵시적으로 반환 가능 classNoResultProcessor : Processor<Unit> { overridefunprocess() { // 업무 처리 코드 } }
classResultProcessor : Processor<Integer> { overridefunprocess(): Int { // 업무 처리 코드 return someInt } }
Kotlin은 왜 void가 아닌 Unit을 만들었을까?
Kotlin의 Unit은 Java의 void와 다르게 두 가지 특징을 가진다
Unit은 싱글톤 인스턴스이다. 그래서 Kotlin에서 Unit이라는 키워드는 타입이면서도 동시에 객체
1
val unit : Unit = Unit
Unit은 객체이기도 하기때문에 Any를 상속하는 서브 클래스이다.
1
val unit : Any = Unit
2.5 Nothing 타입: 이 함수는 결코 정상적으로 끝나지 않는다.
함수가 정상적으로 끝나지 않는 사실을 표현할 때 사용
Nothing 타입은 아무 값도 포함하지 않음 = return이라는 행위 자체를 하지않음
Ex. 1) 함수가 리턴 될 일이 없을 경우
1 2 3 4 5
funinfiniteLoop(): Nothing { while (true) { println("Hi there!") } }
funreadNumbers(reader: BufferedReader): List<Int?> { val result = ArrayList<Int?>() for (line in reader.lineSequence()) { try { val number = line.toInt() result.add(number) } catch(e: NumberFormatException) { result.add(null) } } return result }
funaddValidNumbers(numbers: List<Int?>) { var sumOfValidNumbers = 0 var invalidNumbers = 0 for (number in numbers) { if (number != null) { sumOfValidNumbers += number } else { invalidNumbers++ } } println("Sum of valid numbers: $sumOfValidNumbers") println("Invalid numbers: $invalidNumbers") }
funmain(args: Array<String>) { val reader = BufferedReader(StringReader("1\nabc\n42")) val numbers = readNumbers(reader) addValidNumbers(numbers) //Sum of valid numbers: 43 //Invalid numbers: 1 }
List<Int?> 과 List?과 List<Int?>? 잘 구분해서 쓸 것!
3.2 읽기 전용과 변경 가능한 컬렉션
Kotlin 컬렉션은 자바와 다르게 읽기 전용 컬렉션(Collection)과 변경가능 컬렉션(MutableCollection)이 분리되어있다.
//MutableCollection이 Collection 참조 val collection: Collection<Int> = arrayListOf(1, 2, 3) val mutableCollection: MutableCollection<Int> = collection error: type mismatch: inferred type is Collection<Int> but MutableCollection<Int> was expected
// Collection이 MutableCollection 참조 val mutableCollection: MutableCollection<Int> = arrayListOf(1, 2, 3) val collection: Collection<Int> = mutableCollection //가능 >>> collection [1, 2, 3] >>> mutableCollection [1, 2, 3] >>> mutableCollection.add(5) >>> mutableCollection [1, 2, 3, 5] >>> collection [1, 2, 3, 5] // mutalbeCollection에 5를 추가했지만 collection에도 영향을 미친다 // 같은 객체를 다른 타입의 참조들이 가리키고 있다.
따라서, ConcurrentModificationException등의 오류가 발생할 수 있으므로, 다중 스레드 환경에서 데이터를 다루는 경우 그 데이터를 적절히 동기화 하거나 동시 접근을 허용하는 데이터 구조를 활용해야한다.
3.3 코틀린 컬렉션과 자바
코틀린에서 읽기 전용인 Collection으로 선언된 객체라도 자바코드에서는, 이를 구분하지 않으므로 수정가능
코틀린 컴파일러가 컬렉션이 어떤 일을 하는지 정확한 분석이 어려움
어차피 ImmutableList가 나와도 자바로 가면 변경이 가능하니 차라리 변경 가능한 컬렉션을 넘기고 명시를 정확히 해두는 것이 좋다.
1 2 3 4 5 6 7 8 9 10
//Java publicclassCollectionUtils{ publicstatic List<String> uppercaseAll(List<String> items){ for (int i = 0; i < items.size(); i++) { items.set(i, items.get(i).toUpperCase()); } return items; } }