일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- c++
- 웹해킹
- backend
- 딥러닝
- mysql
- 백엔드
- deep learning
- 경제
- Web
- php
- Scala
- ChatGPT
- Linux
- hacking
- flask
- c
- 리눅스
- 파이썬
- Python
- webhacking
- 러닝 스칼라
- 러닝스칼라
- 인공지능
- BOF
- hackerschool
- BOF 원정대
- Shellcode
- 챗GPT
- Javascript
- hackthissite
- Today
- Total
jam 블로그
러닝스칼라 8장 - 클래스 본문
클래스란?
데이터 구조와 함수의 조합으로, 객체지향 언어의 핵심 구성 요소
특징
상속(inheritance)
: 다른 클래스로 확장할 수 있어서 서브 클래스와 슈퍼 클래스의 계층 구조 생성 가능다형성(polymorphism)
: 서브 클래스가 부모 클래스를 대신하여 작업하는 것이 가능캡슐화(encapsulation)
: 클래스의 외관을 관리하는 프라이버시 제어를 제공
class User
val u = new User
val u1 = new User
val isAnyRef = u.isInstanceOf[AnyRef]
/*
defined class User
u: User = User@4c478184
u1: User = User@2e6ef924
isAnyRef: Boolean = true
*/
AnyRef는 classes의 부모 타입입니다.
클래스 기본
**// 1번 예제**
class User {
val name: String = "jmkim"
def greet: String = s"Hello from $name"
override def toString = s"User($name)"
}
val u = new User
println(u.greet)
/*
defined class User
u: User = User(jmkim)
Hello from jmkim
*/
toString을 override 함으로써 기존에 User@4c478184 처럼 보여지는 것을
u: User = User(jmkim)처럼 변경 됩니다.
**// 2번 예제**
class User(val name: String) {
def greet: String = s"Hello from $name"
override def toString = s"User($name)"
}
val u = new User("jmkim")
println(u.greet)
/*
defined class User
u: User = User(jmkim)
Hello from jmkim
*/
1번 예제와 다르게 name을 매개 변수로 받으면서 바로 클래스 필드로 사용하는 방법입니다.
다음은 리스트를 사용하여 다양한 것을 해봅니다.
val users = List(new User("jam"), new User("karl"), new User("jason"), new User("sophie"), new User("kai"), new User("lucas"), new User("jeff"))
val usersSize = users.size
val userNameSize = users map (_.name.length)
val sorted = users sortBy (_.name)
val jason = users find (_.name contains "jason")
val greet = jason map (_.greet) getOrElse "Hi"
val test = users find (_.name contains "test")
val greet1 = test map (_.greet) getOrElse "Hi"
/*
users: List[User] = List(User(jam), User(karl), User(jason), User(sophie), User(kai), User(lucas), User(jeff))
usersSize: Int = 7
userNameSize: List[Int] = List(3, 4, 5, 6, 3, 5, 4)
sorted: List[User] = List(User(jam), User(jason), User(jeff), User(kai), User(karl), User(lucas), User(sophie))
jason: Option[User] = Some(User(jason))
greet: String = Hello from jason
test: Option[User] = None
greet1: String = Hi
*/
클래스 상속, 다형성
상속
: 서브 클래스가 부모 클래스로부터 필드와 메소드를 extends 키워드로 확장된 것이며, override로
상속받은 메소드를 재정의 할 수 있습니다.
class A {
def hi = "Hello from A"
override def toString = getClass.getName
}
class B extends A
class C extends B {
override def hi = "hi C -> " + super.hi
}
val hiA = new A().hi
val hiB = new B().hi
val hiC = new C().hi
/*
defined class A
defined class B
defined class C
hiA: String = Hello from A
hiB: String = Hello from A
hiC: String = hi C -> Hello from A
*/
다형성
: 서브 클래스는 자신의 부모 클래스를 확장하므로 부모의 필드와 메소드를 100% 지원하지만 그 역은 성립하지 않을 수 도 있습니다.
class A {
def hi = "Hello from A"
override def toString = getClass.getName
}
class B extends A
class C extends B {
override def hi = "hi C -> " + super.hi
}
val a: A = new A
val a: A = new B
val b: B = new B
val b: B = new A
/*
defined class A
defined class B
defined class C
a: A = A
a: A = B
b: B = B
Error:(14, 12) type mismatch;
found : A
required: B
val b: B = new A
*/
클래스 정의하기
입력 매개변수로 클래스 정의하기
class Car(val make: String, var reserved: Boolean) {
def reserve(r: Boolean):Unit = {reserved = r}
}
val t = new Car("Tesla", false)
t.reserve(true)
println(s"My ${t.make} is now reserved? ${t.reserved}")
/*
defined class Car
t: Car = Car@2c4c333a
My Tesla is now reserved? true
*/
val t2 = new Car(reserved = false, make = "Toyota")
println(t2)
/*
t2: Car = Car@20a0dd40
My Toyota is now reserved? false
*/
class Lotus(val color: String, reserved: Boolean) extends Car("Lotus", reserved)
val l = new Lotus("sliver", false)
println(s"Requested a ${l.color} ${l.make}")
/*
defined class Lotus
l: Lotus = Lotus@6067b0f0
Requested a sliver Lotus
*/
입력 매개변수와 기본값으로 클래스 정의하기
class Car(val make: String, var reserved : Boolean = true, val year: Int = 2005) {
override def toString = s"$year $make, reserved = $reserved"
}
val a = new Car("Acura")
val l = new Car("Lexus", year = 2010)
val p = new Car(reserved = false, make = "Porsche")
/*
a: Car = 2005 Acura, reserved = true
l: Car = 2010 Lexus, reserved = true
p: Car = 2005 Porsche, reserved = false
*/
그 외의 클래스 유형
추상 클래스
추상 클래스
는 다른 클래스들에 의해 확장되도록 설계되었으나 인스턴스를 생성하지 않는 클래스
서브클래스들이 필요로 하는 핵심적인 필드와 메소드를 실제 구현물을 제공하지 않으면서 정의하는 데에 사용
abstract class Car {
val year: Int
val automatic: Boolean = true
def color: String
}
class Mini(val year: Int, val color: String) extends Car
val redMini: Car = new Mini(2005, "Red")
println(s"Got a ${redMini.color} Mini")
/*
defined class Car
defined class Mini
redMini: Car = Mini@19fce221
Got a Red Mini
*/
익명 클래스
익명클래스
는 서브 클래스가 한번만 필요한 상황일 때 코드를 단순화 해주는 데에 도움을 줍니다.
추상 클래스를 따로 extends로 상속 받는 class를 만들지 않고 notification.register에 있는 것처럼
new Listener로 정의하여 익명 클래스를 만들어 넣습니다.
abstract class Listener {def trigger}
class Listening {
var listener: Listener = null
def register(l: Listener) {listener = l}
def sendNotification() {listener.trigger}
}
val notification = new Listening()
notification.register(new Listener {
def trigger {println(s"Trigger at ${new java.util.Date}")}
})
notification.sendNotification()
/*
defined class Listener
defined class Listening
notification: Listening = Listening@1db58f45
Trigger at Sat Jun 20 14:17:07 KST 2020
*/
그외의 필드와 메소드 유형
중복 정의된 메소드
동일한 이름과 반환값을 갖지만, 다른 입력 매개변수를 갖는 둘 또는 그 이상의 메소드를 가질 수 있습니다.
class Printer(msg: String) {
def print(s:String):Unit = println(s"$msg: $s")
def print(l:Seq[String]):Unit = print(l.mkString(", "))
}
new Printer("Today's Report").print("Foggy"::"Rainy":: "Hot" :: Nil)
/*
defined class Printer
Today's Report: Foggy, Rainy, Hot
*/
apply 메소드
apply
메소드는 기본 메소드 또는 인젝터 메소드로 불리며 메소드 이름 없이 호출 될 수 있습니다.
class Multiplier(factor: Int) {
def apply(input: Int) = input * factor
}
val tripleMe = new Multiplier(3)
val tripled = tripleMe.apply(10)
val tripled2 = tripleMe(10)
/*
defined class Multiplier
tripleMe: Multiplier = Multiplier@43e42b0b
tripled: Int = 30
tripled2: Int = 30
*/
지연값
지연값
은 자신이 처음 인스턴스화될 때에만 생성되며 val 키워드 앞에 lazy
를 씁니다.
클래스 수명 내에 실행 시간과 성능에 민감한 연산이 한 번만 실행 될 수 있음을 보장하며,
보통 파일 기반의 속성, 데이터 베이스 커넥션 열기, 그 외 정말 필요한 경우 단 한번만 초기화 되어야 하는
불변의 데이터와 같은 정보를 저장하는데 사용합니다.
class RandomPoint {
val x = {println("creating x"); util.Random.nextInt}
lazy val y = {println("now y"); util.Random.nextInt}
}
val p = new RandomPoint()
println(s"Location is ${p.x}, ${p.y}")
println(s"Location is ${p.x}, ${p.y}")
/*
defined class RandomPoint
creating x
p: RandomPoint = RandomPoint@6ecd22fd
now y
Location is 389314740, -1884004327
Location is 389314740, -1884004327
*/
패키징
패키지
는 스칼라 코드를 점으로 구분된 경로를 사용하여 디렉토리별로 정리할 수 있게 해줍니다.
mkdir -p src/com/oreilly
cat > src/com/oreilly/Config.scala
# class Config(val baseUrl:String = "http://localhost")
scalac src/com/oreilly/Config.scala
ls com/oreilly/Config.class
# com/oreilly/Config.class
패키징된 클래스에 접근하기
import java.util.Date
val d = new Date
import scala.collection.mutable._
// import scala.collection.mutable.{ArrayBuffer => ArrBuf, Queue}
val b = new ArrayBuffer[String]
b += "Hello"
val q = new Queue[Int]
q.enqueue(3,4,5)
val pop = q.dequeue
println(q)
/*
import scala.collection.mutable._
b: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer()
res0: b.type = ArrayBuffer(Hello)
q: scala.collection.mutable.Queue[Int] = Queue()
res1: q.type = Queue(3, 4, 5)
pop: Int = 3
Queue(4, 5)
*/
패키징 구문
아래와 같이 package를 사용하여 만들 수 있습니다.
package com {
package oreilly {
class Config(val baseUrl: String = "http://localhost")
}
}
val url = new com.oreilly.Config().baseUrl
프라이버시 제어
기본은 pubilc이며 상황에 따라서 protected, private를 사용하여 제어를 할 수 있습니다.
protected
: 정의한 클래스와 서브 클래스의 코드에서만 접근이 가능합니다.private
: 정의한 클래스에서만 접근이 가능합니다.
class User(private var password: String) {
def update(p: String): Unit = {
println("Modifying the password!")
password = p
}
def validate(p: String) = p == password
}
val u = new User("1234")
val isValid = u.validate("4567")
u.update("4567")
val isValid = u.validate("4567")
/*
defined class User
u: User = User@2030ce87
isValid: Boolean = false
Modifying the password!
isValid: Boolean = true
*/
프라이버시 접근 변경자
package com.oreilly {
// []로 접근 블록을 지정해 줄 수 있습니다.
private[oreilly] class Config {
val url = "http://localhsot"
}
class Authentication {
private[this] val password = "jason"
def validate = password.nonEmpty
}
class Test {
println(s"url = ${new Config().url}")
}
}
object Example extends App {
val valid = new com.oreilly.Authentication().validate
println(valid)
new com.oreilly.Test
/*
true
url = http://localhsot
*/
}
종단 클래스와 봉인 클래스
종단 클래스(final class)
는 더이상 상속을 받지 못하게 만들며, 변수에 final을 쓸 경우 override가 방지 됩니다.
봉인 클래스(sealed class)
는 클래스의 서브클래스가 부모 클래스와 동일한 파일에 위치하도록 제한 합니다.
또한, 클래스의 계층 구조에 대해 안전하게 가정하는 코드를 작성 할 수 있습니다.
// 같은 파일 안에 class를 상속 할 경우
sealed class Fruit(color: String) {
def printColor = println(color)
}
class Apple extends Fruit("Red") {
def print = "Apple"
}
// 다른 파일에서 상속 할 경우
class Banana extends Fruit("Yellow") {
}
// illegal inheritance from sealed class Fruit
'IT Book Study > 러닝 스칼라' 카테고리의 다른 글
러닝스칼라 10장 - 고급 타입 특징 (0) | 2020.09.28 |
---|---|
러닝스칼라 9장 - 객체, 케이스 클래스, 트레이트 (0) | 2020.09.28 |
러닝스칼라 7장 - 그 외의 컬렉션 (0) | 2020.09.28 |
러닝스칼라 6장 - 보편적인 컬렉션 (0) | 2020.09.28 |
러닝스칼라 5장 - 일급 함수 (0) | 2020.09.28 |