일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- CORDA
- hyperledger fabric
- akka 강좌
- 안드로이드 웹뷰
- Akka
- 플레이프레임워크
- 블록체인
- Hyperledger fabric gossip protocol
- 주키퍼
- Play2 로 웹 개발
- 엔터프라이즈 블록체인
- 하이퍼레저 패브릭
- 파이썬 강좌
- Golang
- Actor
- play2 강좌
- 파이썬 데이터분석
- 파이썬 동시성
- 하이브리드앱
- Play2
- 파이썬
- 스위프트
- 스칼라
- 파이썬 머신러닝
- Adapter 패턴
- 그라파나
- play 강좌
- 스칼라 강좌
- 스칼라 동시성
- 이더리움
- Today
- Total
HAMA 블로그
스칼라 강좌 (13) - 클래스와 객체 본문
클래스와 객체
이런 클래스 가 있을때
new ChecksumAccumulator // 이렇게 해주면 객체가 만들어 진다.
클래스 안에는 필드와 메소드를 넣을 수 있다. 이 둘을 합쳐 멤버라고 한다.
필드는 var 이나 val 로 정의하며
메소드는 def 로 정의 한다.
class ChecksumAccumulator {
var sum = 0
}
위의 클래스를 가지고 객체를 2개 만들면
val a = new ChecksumAccumulator
val b = new ChecksumAccumulator
해당 객체안의 sum 필드는 다른 메모리를 참조 할 것이고 0 을 바라 볼 것이다.
sum 이 var 이기 때문에, 나중에 다른 Int 값을 재할당 할 수 있다.
a.sum = 3 // 요렇게
객체가 val 인데도 불구하고 내부의 값을 바꿀 수 있다는 점을 체크하라.
다만 처음 생성한 그 객체를 바라보고 있다는 확신은 가질 수 있다.
필드 접근성
* 필드를 비공개로 만들어서 직접 접근하지 못하게 하라. private 을 사용한다.
- 아무것도 안붙히면 전체 공개. (자바와 다르다)
메소드 특성
* 스칼라 메소드 파라미터에서 중요한 점은 이들이 val 이라는것이다.
def add(b : Byte) : Unit = {
b = 1 // 파라미터가 val 이라 불가능 !
sum += b
}
* return 을 명시적으로 사용하지 말라.
스칼라는 메소드가 한 값을 계산하는 표현식인 것 처럼 생각하는걸 권장한다.
* 메소드가 오직 하나의 표현식만 계산하는 경우 {} 중괄호를 없앨 수 있다.
def add(b:Byte) : Unit = sum += b
* 메소드의 결과 타입이 Unit 인 경우는 부수 효과를 위해 사용한다는 뜻이다.
* Unit 생략하고 = 를 없애서 {} 로 메소드를 나타낼 수 있는데, 이건 프로시저 처럼 보이게 하는데
프로시저란 오직 부수 효과를 얻기 위해서만 사용하는 메소드를 의미한다.
즉 def add(b: Byte) { sum += b} 처럼 사용한다는 뜻이데, 여기서 함정이 있는게
def add2 () { " test string " } 이건 Unit 를 리턴하며
def add3 () = { " test string " } 이건 String 을 리턴한다는 것이다.
즉 프로시저 처럼 작성하면 리턴이 무조건 Unit 이 된다는점~
세미콜론 추론
스칼라에서는 보통 문장 끝의 세미콜론(;) 을 생략 할 수 있다.
다만 한 줄에 여러 문장이라면 사용해야한다.
가끔 헷갈리는것은
x
+ y
스칼라는 위 문장을 x 와 +y로 파싱한다.
(x
+y) 이렇게 하거나
+ 를 줄의 끝에 넣으면 우리가 의도하는대로 된다.
x +
y
싱글톤 객체
스칼라와 자바의 가장 큰 차이점 중 하나는 스칼라는 정적 멤버가 없다는 것이다.
대신 전용으로 사용할 수 있는 싱글톤 객체를 제공한다.
class 대신 object 라는 키워드를 사용한다.
import scala.collection.mutable.Map
object ChecksumAccumulator {
private val cache = Map[String, Int) ()
def calculate(s: String) : Int =
if (cache.contains(s))
cache(s)
else {
val acc = new ChecksumAccumulator
for (c <- s)
acc.add(c.toByte)
val cs = acc.checksum()
cache += (s -> cs) // s 키, cs 값 으로 연관관계를 만듬.
cs
}
}
이 싱글톤 객체는 이름이 저 위에 있는 class ChecksumAccumulator 와 같다.
이렇게 싱글톤 객체는 동일하게 만들 수 있는데 이걸 동반 객체라고 하며
같은 파일안에 있어야한다.
이런 클래스와 동반객체는 상대방의 비공개 멤버에 접근 할 수 있게 된다.
즉 자바의 정적멤버가 외부에 따로 만들어져 있다고 보면 된다.
ChecksumAccumulator.calculate("hello") 이렇게 호출 할 수 있다.
* 팁: 코드에서 맵 경우 WeakHashMap 같은 걸 사용하면 메모리가 부족할때 가비지콜렉터가 캐시의 원소를 수집.
- 싱글턴 객체는 파라미터를 받을 수 없고 클래스는 받을 수 있다.
- 동반 클래스가 없는 싱글톤 클래스를 독립 객체라고 한다.
독립객체로는 유틸리티 메소드를 모아두는곳 이나 진입점으로 사용할 수 있다.
스칼라 애플리케이션
import ChecksumAccumulator.calculate
object Sumer {
def main(args: Array[String]){
for (arg <- args)
println(arg : ": " + calculate(arg() ) // 메소드만 사용함!
}
}
* 스칼라는 항상 java.lang 과 scala 패키지의 멤버를 암시적으로 임포트한다.
또한 Predef 이라는 싱글톤 객체도 임포트하는데 여기엔 유용한 메소드가 많이 있다.
println 이라든지 assert 라든지..
* 스칼라의 import 는 자바의 static import 로 생각 할 수 있다. 스칼라에서 다른 점은 싱글톤 뿐 아니라 어느 객체에서라도 멤버를 임포트 할 수 있다는 것이다.
* 자바는 공개 클래스 이름과 파일 이름이 같아야 하지만 스칼라에서는 상관없다.
* scala.Application 이라는 트레이트를 통해 더 간단히 main 을 작성 할 수 있다.
import ChecksumAccumulator.calculate
object Sumer extends Application {
for (arg <- List("fall","winter"))
println(arg : ": " + calculate(arg() ) // 메소드만 사용함!
}
클래스 예제
1) 간단한 클래스
# 한줄 만으로 class 완성!
class Person(var name: String, var age: Int) // 기본 생성자가 class 이름과 나란히~
# class 사용
val al = new Person("Al", 42)
al.name // "Al"
al.age // 42
2) 자바와 다른 특이한 모습
// class 정의
class Person(val firstName: String, val lastName: String) {
println("the constructor begins") // 희안하죠? 객체로 만들어지는 동시에 호출됩니다.
val fullName = firstName + " " + lastName
val HOME = System.getProperty("user.home");
def foo { println("foo") }
def printFullName { println(fullName) }
printFullName // 희안하죠? 객체로 만들어지는 동시에 호출됩니다.
println("still in the constructor") // 희안하죠? 객체로 만들어지는 동시에 호출됩니다. }
// 다음과 같이 나옵니다. 클래스 내부의 것들이 생성됨과 동시에 실행되네요.
scala> val p = new Person("Alvin", "Alexander")
the constructor begins
Alvin Alexander
still in the constructor
p: Person = Person@68f507d2
3) 보조 생성자
this 를 사용하여 보조적 생성자를 만들수 있네요. (클래스 이름가지고 만들지 않음)
class Pizza { // 기본 생성자에는 매개변수를 받는게 없군요.
var crustSize = 12 var crustType = "Thin"
def this(crustSize: Int) { // 매개변수 하나 받는 생성자~
this()
this.crustSize = crustSize
}
def this(crustSize: Int, crustType: String) { // 매개변수 2개 받는 생성자
this(crustSize)
this.crustType = crustType
}
override def toString = { "A %s inch pizza with %s crust.".format(crustSize, crustType) } }
println(new Pizza)
println(new Pizza(14))
println(new Pizza(16, "Thick"))
4) 생성자 매개변수에게 디폴트 값을~
class Socket (var timeout: Int = 10000) // 디폴트 값을 사용함
val s = new Socket s.timeout // Int = 10000
val s = new Socket(5000) s.timeout // Int = 5000
5) 추상 클래스를 사용할 경우는 언제?
추상클래스는 생성자 매개변수를 가질수 있지만, trait 는 그럴 수 없다.
* trait 는 자바의 인터페이스 비슷한 것 으로 추후에 설명 예정.
// 컴파일 안됨 trait Animal(name: String)
// 추상 클래스는 가능 ! abstract class Animal(name: String)
abstract class BaseController(db: Database) {
def save { db.save }
def update { db.update }
def delete { db.delete }
def connect // abstract since it has no implementation
def getStatus: String // an abstract method that returns a String
def setServerName(serverName: String) // an abstract method that takes a parameter
}
6) 스칼라 case 클래스
case 클래스는 유용한 행사코드를 자동으로 생성할 수 있다.
case class Person(var name: String, var age: Int) // class 앞에 case 를 붙여주는것으로
case class Person(var name: String, var age: Int) // class 앞에 case 를 붙여주는것으로
case class 를 사용하면 다양한 혜택과 자동으로 다음을 만들어 준다:
- toString, equals, 와 hashCode 메소드를 만들어 준다.
- 생성자 파라미터들에게 게터와 세터 속성이 부여된다.
- 더이상 인스턴스로 만드는데 new 를 사용할 필요가 없다.
- match/case 문에서 편리하게 이용할 수 있다.
'Scala' 카테고리의 다른 글
스칼라 강좌 (15) - Getter / Setter (0) | 2016.07.31 |
---|---|
스칼라 강좌 (14) - 함수 와 메소드 (0) | 2016.07.23 |
스칼라 강좌 (12) - 객체의 동일성 (0) | 2016.07.08 |
스칼라 강좌 (11) - while 루프 와 재귀 (0) | 2016.07.03 |
스칼라 강좌 (10) - 공변성,불변성,역공변성 (0) | 2016.06.27 |