함수는 기능구현의 단위이며 호출시 블럭내 코드를 수행하게 된다.
함수의 구조는 다음과 같다.
fun 함수이름(파라미터 이름: 파라미터 타입): 리턴 타입 {
}
fun plus(x: Int, y: Int): Int {
return x + y
}
fun 함수이름(파라미터 이름: 파라미터 타입) {
}
fun printSum(x: Int, y: Int) {
println("x + y = ${x + y}")
}
fun 함수이름(): 리턴 타입 {
}
fun getPi(): Double {
return 3.14
}
fun 함수이름() {
}
fun hello() {
println("hello world")
}
파라미터가 있는 함수를 사용할 때 Default parameter를 설정하면
입력값이 없는 경우에도 기본값으로 동작을 할 수 있게 해준다.
fun showInfo(name: String, age: Int = 30, weight: Double = 70.5) {
println("이름은 ${name}")
println("나이는 ${age}")
println("몸무게는 ${weight}")
}
두번째 default parameter: age = 30
세번째 default parameter: weight = 70.5
기본값들을 설정하면 다음과 같이 함수를 호출해서 사용할 수 있다.
showInfo("Michael", 29, 65.5)
showInfo("Michael")
showInfo("Michael", weight = 67.5)
클래스는 변수와 함수의 모음이다.
클래스의 변수를 프로퍼티, 클래스 함수를 메소드라고 부른다.
class Student {
var name: String = "홍길동"
fun printName() {
println("이름은 ${name}입니다.")
}
}
var kim = Student()
kim.printName()
Student라는 클래스를 만들고나서 생성자를 호출하여 변수에 담는다. (인스턴스화)
인스턴스 생성후 메소드를 호출할 수 있다.
프로퍼티는 default 값으로 저장되어있는 것을 확인할 수 있다.
class Student {
constructor(name: String, id: Int) {
this.name = name
this.id = id
}
var name: String = "홍길동"
var id: Int = 0
fun printInfo() {
println("이름은 ${name}입니다.")
println("학번은 ${id}입니다.")
}
}
var kim = Student("김민수", 1234)
kim.printInfo()
생성자를 constructor로 정의하면 인스턴스 생성과 동시에 프로퍼티를 원하는 값으로 초기화가 가능하다.
this는 자신이 생성할 인스턴스를 가리키며 이를 통해 프로퍼티에 접근할 수 있다.
클래스가 생성자를 호출해야 메모리를 할당받는 것과 달리
오브젝트는 프로그램 시작과 동시에 메모리를 할당받는다.(static 클래스)
그래서 오브젝트 이름자체로 프로퍼티, 메소드에 접근한다.
object Person {
var name: String = "홍길동"
fun printName() {
println("이름은 ${name}입니다.")
}
}
Person.printName()
오브젝트이름으로 메소드 호출이 가능하다.(생성자 불필요)
클래스 전체를 static으로 사용하지 않고
부분적으로만 static으로 사용하고 싶은 경우가 있다.
이때는 클래스 내부 companion object 선언을 하면 해당 부분만 static 영역으로 사용할 수 있다. (static 프로퍼티, static 메소드)
class Person {
companion object {
var name: String = "홍길동"
fun printName() {
println("이름은 ${name}입니다.")
}
}
fun greeting() {
println("안녕하세요")
}
}
var kim = Person()
Person.name = "김민수"
Person.printName()
kim.greeting()
클래스 메소드는 인스턴스(kim)을 통해서 접근하지만
static 프로퍼티, static 메소드는 클래스 이름(Person)을 통해 접근한다.
데이터 클래스는 값의 저장용도로 사용한다.
data class UserData(val name: String, var age: Int)
var user = UserData("Michael", 20)
user.age = 30
println("이름:${user.name}, 나이:${user.age}")
println("${user.toString()}")
일반적으로 코드 블럭을 사용하지 않고 간단하게 사용하는 경우가 많다.
상속은 코드를 재사용하며 체계적으로 관리할 수 있는 방법이다.
자식 클래스를 만들때 부모 클래스를 지정하면 부모클래스의 메소드와 프로퍼티를 사용할 수 있다.
open class Person
{
var name: String = "홍길동"
fun showInfo()
{
println("이름은 ${name}")
}
}
class Student : Person()
{
var id: Int = 0
fun showId()
{
println("학번은 ${id}")
}
}
var kim = Student()
kim.name = "김민수"
kim.id = 1234
kim.showInfo()
kim.showId()
부모클래스는 open 키워드를 붙여 생성한다.
class 자식클래스이름 : 부모클래스이름()
과 같이 부모클래스를 지정한다.
자식 클래스 인스턴스를 생성하면 부모클래스의 프로퍼티와 메소드를 가지고 있는 것을 확인할 수 있다.
open class Person {
var name: String = "홍길동"
open fun showInfo() {
println("이름은 ${name}")
}
}
class Student : Person() {
var id: Int = 0
override fun showInfo() {
println("이름은 ${name}")
println("학번은 ${id}")
}
}
var kim = Student()
kim.name = "김민수"
kim.id = 1234
kim.showInfo()
부모클래스의 프로퍼티 또는 메소드를 open 키워드를 붙이고
자식클래스의 프로퍼티 또는 메소드는 override 키워드를 붙임으로써 재정의를 할 수 있다.
자식 클래스 인스턴스의 메소드를 호출하면 재정의된 함수가 호출되는 것을 확인할 수 있다.
추상화는 설계도면이라고 생각하면 좋다.
인터페이스, 추상클래스로 큰 틀을 설계한 후에 이를 상속받은 클래스에서 구체적인 내용을 구현한다.
interface Animal {
var name: String
fun move()
}
class Bird : Animal {
override var name: String = "새"
override fun move() {
println("${name}가 날아서 이동합니다.")
}
}
var bird = Bird()
bird.move()
인터페이스를 상속받은 클래스는 프로퍼티와 메소드를 모두 오버라이드해서 구현해야한다.
abstract class Animal {
abstract var name: String
abstract fun move()
fun walk() {
println("${name}가 걷습니다.")
}
}
class Bird : Animal() {
override var name: String = "새"
override fun move() {
println("${name}가 날아서 이동합니다.")
}
}
var bird = Bird()
bird.walk()
실제로 구현한 코드가 포함이 된다면 인터페이스 대신에 추상클래스를 사용한다. 그외 내용은 인터페이스와 동일하다.
익스텐션은 미리 만들어져 있는 클래스에 메소드를 추가하는 개념이다.
fun String.customPlus(word: String): String {
return this + word
}
var org = "Hello"
var added = "World"
println("결과: ${org.customPlus(added)}")
기존에 있는 클래스 String에 customPlus 새로운 메소드가 추가된 것을 확인할 수 있다.