5장 GO의 타입 시스템
Go는 정적 타입 프로그래밍 언어
primitive 타입의 경우 이름에 바이트 크기가 같이 있는 경우가 많음. (int32, int64 등)
사용자 정의 타입 (composite type)
c의 struct랑 비슷
변수로 선언하면 모든 필드가 제로값으로 초기화됨
제로값이 아니라 특정 값으로 초기화되어야하면 구조체 리터럴 및 변수 선언 연산자 사용
var1 := myType{value1, value2}
중첩 가능
primitive 타입을 base type으로 새로운 타입 정의 가능
type Duration int64
base type과 완전히 다른 별개의 타입으로 취급
Method
타입에 행위를 정의하기 위한 방법. func 키워드와 함수 이름 사이에 추가 패러미터를 정의한 함수
func와 함수 이름 사이의 패러미터는 특별히 receiver이라고 부름
그리고 이러한 receiver가 있는 함수는 method라고 지칭
method의 경우 일반적인 함수의
Package.Function
대신에Type.Function
형태로 호출이 타입이 method의 receiver으로 전달됨.
Value receiver vs Pointer receiver
Value receiver
func (n myType) method() {}
receiver의 값을 복사해서 method로 전달함. 따라서 method 내부에서 아무리 변경하더라도 원본에는 영향이 없음.
pointer로도 바로 호출이 가능함. pointer를 사용해서 method를 호출하면 실질적으로는
(*myPointer).method()
와 동일불변 객체를 유지하고싶다면 Value receiver가 답. 변경이 발생할 때마다 복제본이 반환됨.
Pointer receiver
func (n *myType) method() {}
receiver의 pointer를 복사해서 method로 전달. 덕분에 method 내부에서 변경하면 원본 인스턴스의 값도 같이 변경됨
value으로도 바로 호출이 가능함. value을 사용해서 method를 호출하면
(&myValue).method()
와 동일기존 인스턴스의 상태를 변경해야한다면 Pointer receiver가 답. 변경이 발생하면 원본이 변경됨.
내장 타입 (built-in type)
언어 차원에서 지원되는 타입들
숫자, 문자열, boolean 등
primitive type이라고 말할 수도 있음
뭔가 추가하거나 제거하면 기본값이 변경되지 않고 반드시 새로운 값 생성
함수나 메소드에 전달할 때 복사된 value이 전달됨
built-in type에 대해서 method를 선언할 수 없음
built-in type을 base type으로 새로운 사용자 정의 타입을 선언해야함
참조 타입 (reference type)
슬라이스, 맵, 채널, 인터페이스, 함수 타입 등
변수와 함께 헤더 value 생성
헤더는 해당 타입의 데이터 구조를 관리하기 위해 여러 pointer, value들을 필드로 가지고 있음
슬라이스로 치면 length, capacity 및 내장 배열 pointer 등
헤더는 함수로 전달하거나 리시버로 호출할 때 value 복사를 전제로 깔고 설계되었기 때문에 pointer로 쓰면 안됨
문자열 또한 기술적으로는 참조 타입
구조체 타입
primitive, non-primitive 성질을 모두 가질 수 있음
원하는 성질에 따라 built-in type, reference type을 위한 가이드라인을 준수해야 함
가변 객체의 경우 메소드나 함수, 리시버에서 pointer를 사용
불변 객체의 경우 메소드나 함수, 리시버에서 value 사용
인터페이스 value이 다른 경우 value 타입 리시버에게 유연성을 제공해야하는게 유일한 예외
인터페이스 타입
다형성을 위해서 사용
행위를 선언하기 위한 타입
사용자 정의 타입이 인터페이스에 정의된 행위를 메소드 형태로 구현해야 함 <- 구현 타입
인터페이스 메소드를 구현한 사용자 정의 타입은 (== 메소드 집합에 동일하게 모두 있어야 함) 인터페이스 value에 대입 가능
인터페이스는 2개의 word로 구성
첫번째 word는 iTable에 대한 주소. iTable에는 저장된 데이터의 타입 정보, 메소드 집합 저장
두번째 word는 실제 value에 대한 주소
메소드 집합
주어진 타입 value이나 pointer에 관련된 메소드 집합. receiver의 종류에 따라 메소드가 value이냐 pointer냐가 정해짐
value는 value receiver를 가진 메소드들만 메소드 집합으로 포함
value의 주소를 확인할 수 없는 경우도 존재하기 때문 -> go에서 자동으로 바꿔서 실행시킬 수 없음
pointer는 value, pointer receiver 모두 메소드 집합으로 포함
다형성
어떤 타입이든 인터페이스를 구현할 수 있음
구현 타입이 인터페이스에 정의된 메소드를 구현하기만 하면 실제 타입이 뭐든간에 자유롭게 전달, 캐스팅이 가능
타입 임베딩
기존의 타입을 확장하거나 동작을 변경
기존에 선언된 타입을 새로운 구조체 타입의 내부에 선언하는 것
새로운 타입은 특별히 outer type, 내부에 들어간 타입은 inner type이라고 지칭
inner type의 필드, 메소드 등 모든 것들이 outer type에게 적용됨. 덕분에 inner type의 구현한 인터페이스는 outer type도 자동으로 구현하게 됨
outer type에서 재정의해서 오버라이딩도 가능
inner type을 직접 접근해서 호출하는 것도 가능
노출, 비노출 식별자
소문자로 시작하면 비노출
대문자로 시작하면 노출
만약 비노출 필드에 대해서 팩토리 함수를 제공한다면
New
로 이름 짓는게 규칙노출되지 않은 타입을 명시적으로 선언하는건 불가능하지만 단축 변수 선언 연산자로 정의하는 것은 가능
식별자는 값이 아니기 때문
단축 변수 선언 연산자는 타입을 추정하여 비노출 타입의 변수를 생성할 수 있기 때문
inner type이 비노출이더라도 outer type이 노출이면 outer type을 통해 접근 가능.
이 경우 구조체 리터럴을 써서 inner type을 초기화할 순 없음
inner type을 직접 호출하는 것도 불가능
Last updated