- public: 명시하지 않으면 기본적으로 public. (어디서나 접근 가능)
- private: 동일한 클래스 내부에서만 접근 가능.
- internal: 같은 모듈 내부(app)에서만 접근 가능.
- protected: 기본적으로 private이지만 상속을 받은경우에 타 모듈에서 접근 가능.
fun main () {
var access = AccessTestClass()
println(access.d) // internal var d = 4
var accessProt = AccessProtected()
accessProt.protectedTest1()
}
class AccessTestClass {
public var a:Int = 1
var b = 2
private var c = 3
internal var d = 4
protected var e = 5
}
class AccessProtected : AccessTestClass() {
fun protectedTest1() {
println("protected e: ${e}") // 상속받은 클래스 내부네서만 쓸 수 있움. + private 기능
}
}
=> 4
protected e: 5
fun main() {
var num_a = readLine()!!.toInt()
}
Exception in thread "main" java.lang.NumberFormatException: For input string: "dskfsjld"
첫줄에 원인이 된 에러가 나온다.
fun main() {
while(true) {
try {
println("숫자를 입력하세요.")
var num_a = readLine()!!.toInt()
println("내가 입력한 숫자는 ${num_a}입니다.")
break
} catch (e: java.lang.NumberFormatException) {
println("숫자가 아닌 문자가 포함되었습니다.")
} finally {
println("오늘 날짜는 23년 12월 6일입니다.")
}
}
}
지연초기화를 활용하면 저사양으로 제한된 환경에서 효율적으로 메모리 사용이 가능하다.
변수 name 값을 바로 넣기 어렵기때문에 lateinit으로 초기화.
변수 사용 전, 초기화되었는지 확인해야 안전성이 높아진다. 그래서 Isinitialized를 활용해 조건문으로 값이 초기화되었는지 알 수 있다. -> true라면 if문 실행.
fun main(){
var s1 = Student()
s1.name = "참새"
s1.displayInfo()
s1.age = 10
s1.displayInfo()
}
class Student {
lateinit var name:String
var age:Int = 0
fun displayInfo() {
if(this::name.isInitialized) { // 클래스 자신의 name을 참조(;;)해서 초기화된건지 아닌건지 true/false 반환.
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.")
} else {
println("name변수를 초기화해주세요.")
}
}
}
더블콜론은 코틀린에서 메소드 참조, 프로퍼티 참조, 클래스를 참조할 때 이용한다.
코틀린에서 변수나 클래스명 앞에 더블론론(::)을 명시하면, 변수나 클래스에 대한 참조를 할 수 있다. 더블콜론을 명시하면 변수가 아닌 객체(Object)로 접근할 수 있기 때문이다.
fun main(){
var s1 = Student()
s1.name = "참새"
s1.displayInfo() // 첫번째 프린트문
s1.age = 10
s1.displayInfo() // 두번째 프린트문
s1.displayInfo() // 세번째 프린트문
}
class Student {
lateinit var name:String
var age:Int = 0
val address: String by lazy {
println("address 초기화")
"seoul"
"Inchoen"
}
fun displayInfo() {
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.")
println("주소는: ${address} 입니다.")
}
}
첫번째 프린트문 println("address 초기화") => val address 프로퍼티를 초기화 할 때 실행된다.(처음 한번만)
이후 두번째, 세번째는 lazy로 나중에 넣은 "Incheon"만 address에 저장된다.
여러 개의 메소드를 호출시켜도 위에 있는 "seoul"이 아닌 맨 아래 lazy가 붙은 "Incheon"만 address에 저장된다.
var name = readLine()!!.toInt()
1) var n = readLine()!!
2) var name = n.toInt()
두 단계로 나눠져 있는데 toInt()는 null값이 들어가면 에러가 나기때문에 !!붙인 것이다.
? (null 저장 가능)
var address:String? = null -> 자료형? : null을 저장할 수 있다.
null을 저장하지 않고 사용하려면 c.지연초기화 - 변수 lateinit 활용하면 된다.
?. (안전 호출연산자 safe-calls)
null인지 아닌지 확인을 한 다음 null이 아닐 때만 호출돼 실행된다. null일 때는 null이 그대로 출력된다.
fun main(){
var s = Student()
s.name = "참새"
s.displayInfo()
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
s.displayAddressLength()
}
class Student {
lateinit var name:String
var address:String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length} 입니다")
// 주소 값이 없어서 길이를 추출하지 못해도 오류가 나지 않고 null 출력됨.
}
}
이름은: 참새 입니다
주소는: null 입니다
주소의 길이는: null 입니다
이름은: 참새 입니다
주소는: 서울 입니다
주소의 길이는: 2 입니다
null이라는 문자열 대신 다른 문자열로 대체할 수 있다.
println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")
-> address가 null인지 아닌지 확인했는데 null이면(?.) null 대신에(?:) 초기화하세요로 바꾸기.
fun main(){
var s = Student()
s.name = "참새"
s.displayInfo()
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
s.displayAddressLength()
}
class Student {
lateinit var name:String
var address:String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")
}
}
이름은: 참새 입니다
주소는: null 입니다
주소의 길이는: 초기화하세요 입니다
이름은: 참새 입니다
주소는: 서울 입니다
주소의 길이는: 2 입니다
class Archer : Character {
var name:String
var age:Int
var gender:String
var money:Int
var hp:Int // 이전에는 초기화를 하도록 클래스 상단에 프로퍼티 값을 적었는데 ex) var hp: Int = 0
constructor(_name:String, _age:Int, _gender:String, _money:Int, _hp:Int) { // construcrtor = 생성자가 있다는 것은 객체가 만들어질 때 이미
// 프로퍼티의 깂이 없는 상황이 없이 항상 정해져 초기화 되기 때문에 생성자를 이용하면 상단에서
// 일일이 프로퍼티의 값을 적지 않고 생략해도 된다.
~~println("${name}궁수 생성") ~~ // -> 이 단계에서는 name값이 정해져 있지 않고 빈 값이기에 에러남.
name = _name
age = _age
gender = _gender
money = _money
hp = _hp
println("${name}궁수 생성") // name이 초기화 되고 값이 정해지면서 사용 가능.
}
override fun attack() {
println("활쏘기!")
}
fun windArrow() {
println("바람의 화살!!")
}
fun windJump(destination:String) {
println("${destination}까지 도약!")
}
}
Any 타입은 코틀린의 최상위 타입이다(Int, String등 모든 자료형, 사용자가 만든 모든 클래스는 여기서 파생된 것이다.) Any 타입 변수는 자료형이 특별히 정해지지 않은 경우에 사용한다.
Any에 ?를 붙인 Any? 는 type들을 반환 받을때 null을 허용해 반환한다는 뜻.
null일때 오류가 나지 않는다. 대신 리턴값으로 null이 들어온다.
타입 뒤에 물음표를 널어주면 null을 허용한 변수라고 선언하는 것이다.
1) null을 대입할 수 없는 변수(not null, 널 불허용)
var data1: Int = 10
data1 = null // 불가능
2) null을 대입할 수 있는 변수(nullable, 널 허용)
var data1: Int? = 10
data1 = null // 가능
e: Exception => 모든 종류의 오류를 포함한 오류 클래스.
안전하게 다 toString()으로 문자열로 받아 Int가 필요한 부분만(ex.money,hp) toInt()로 처리했다.
fun main() {
val worldName = "스코월드"
var myName = inputMyInfo("name").toString()
var myAge = inputMyInfo("age").toString().toInt()
print(myAge)
var myJob = inputMyInfo("job").toString()
var myGender = inputMyInfo("gender").toString()
var myMoney = inputMyInfo("money").toString().toInt()
var myHp = inputMyInfo("hp").toString().toInt()
...
}
inputMyInfo의 매개변수로 type을 던져서 필요한 항목으로 찾아 들어가게 했다.
전달받은 매개변수의 값이 when 조건 안 특정 값(ex. "name")에 맞아야 함수가 실행되어 입력한 값이 반환된다.
fun inputMyInfo(type:String): Any? {
return when(type) {
"name" -> {
println("이름을 입력해주세요")
while(true) {
try {
var originName = readLine()
if(originName?.first() != '_' && originName?.first() != '!') {
return originName
} else {
println("이름을 다시 입력해주세요")
}
} catch(e:Exception) {
println("이름을 다시 입력해주세요")
}
}
}
"age" -> {
println("나이를 입력해주세요")
while(true) {
try {
var originAge:String? = readLine()
return originAge?.toInt() ?: -1
} catch(e:Exception) {
println("나이를 다시 입력해주세요")
}
}
}
"job" -> {
println("직업을 입력해주세요")
while(true) {
try {
var originName = readLine()
if(originName?.equals("궁수") == true || originName?.equals("마법사") == true) {
return originName
} else {
println("직업을 다시 입력해주세요")
}
} catch(e:Exception) {
println("직업을 다시 입력해주세요")
}
}
}
"gender" -> {
println("성별을 입력해주세요")
while(true) {
try {
var originGender = readLine()
if(originGender?.equals("남") == true || originGender?.equals("여") == true) {
return originGender
} else {
println("성별을 다시 입력해주세요")
}
} catch(e:Exception) {
println("성별을 다시 입력해주세요")
}
}
}
"money" -> {
println("초기자본을 입력해주세요")
while(true) {
try {
var originMoney:String? = readLine()
return originMoney?.toInt() ?: -1
} catch(e:Exception) {
println("초기자본을 다시 입력해주세요")
}
}
}
...
else -> {
return "no"
}
}
}
만약 "name" 대신 var myName = inputMyInfo("nameeeeee").toString() 으로 매개변수 type이 "nameeeeee" 로 바뀐다면, println("이름을 입력해주세요") 가 실행되지 않고 age의 <나이를 입력하세요.>출력문이 바로 실행된다.
받지 못한 프로퍼티 값은 else문의 "no"가 대신 들어가게 된다.
Any? 의 ? null 허용 덕분에 null도 리턴 받을 수 있다. ? 빼면 이 코드는 오류가 난다.
else -> {
return null
}
코틀린의 내장 메소드로 배열을 만들어 사용할 수 있다. 리스트화 시켜 인덱스로 빠르케 밸류에 접근할 수 있다. 다른 패키지에 있는 java.util.Arrays 클래스의 메소드여서 반드시 import해야 한다.
fun main() {
var arr = arrayOf(1,2,3,4,5,6,7,8,999)
println(arr) //[Ljava.lang.String;@880ec60
println(Arrays.toString(arr))
println(arr[3])
for ((idx, arr) in arr.withIndex()) {
println("${idx}: ${arr}")
}
}
[Ljava.lang.Integer;@880ec60
[1, 2, 3, 4, 5, 6, 7, 8, 999]
4
0: 1
1: 2
2: 3
3: 4
4: 5
5: 6
6: 7
7: 8
8: 999
arrayOf 메소드를 기반으로 객체([Ljava.lang.Integer;@880ec60)를 리턴헤서 arr에 갖고 있고 arr.withIndex 메소드를 호출하면 위치정보와 값을 하나로 묶어서 리턴해준다. for (index, arr)에서 value 자리에 arr 객체 자신이 꼭 와야 한다.