Golang

Golang 입문기 - 문법 3

kahnco 2023. 11. 20. 23:48

서두

 저번 게시글에서는 Golang의 여러 타입에 대해서 알아보았다. 이번 시간에는 추가적인 Golang 타입에 대해서 알아볼 예정이다.


Interface Types (인터페이스 타입)

 인터페이스 타입은 타입의 집합을 정의한다. 인터페이스 타입의 변수는 인터페이스 타입 집합에 있는 모든 타입의 값을 저장할 수 있다. 이러한 타입을 인터페이스를 구현한다고 칭한다. 인터페이스 타입의 초기화되지 않은 변수 값은 nil이다.

InterfaceType  = "interface" "{" { InterfaceElem ";" } "}" .
InterfaceElem  = MethodElem | TypeElem .
MethodElem     = MethodName Signature .
MethodName     = identifier .
TypeElem       = TypeTerm { "|" TypeTerm } .
TypeTerm       = Type | UnderlyingType .
UnderlyingType = "~" Type .

 

  인터페이스 타입은 인터페이스 요소의 리스트로 구체화될 수 있다. 인터페이스 요소는 하나의 메소드 또는 타입 요소이다. 여기서 타입 요소는 하나 이상의 타입 용어의 통합이다. 타입 용어는 단일 타입이거나 단일 기본 타입이다.


Basic Interfaces (기본 인터페이스)

 가장 기본적인 형태로 인터페이스는 메소드 리스트(비어 있을 수도 있는)를 지정한다. 이러한 인터페이스에 의해 정의된 타입 집합은 해당 메소드를 모두 구현하는 타입 집합이며 해당 메소드 집합은 인터페이스에 의해 지정된 메소드로 정확하게 구성된다. 타입 집합이 메소드 목록으로 완전히 정의될 수 있는 인터페이스를 기본 인터페이스라고 한다.

// A simple File interface.
interface {
	Read([]byte) (int, error)
	Write([]byte) (int, error)
	Close() error
}

 

 명시적으로 지정된 각 메소드의 이름은 고유해야 하며 공백이 아니어야 한다.

interface {
	String() string
	String() string  // illegal: String not unique
	_(x int)         // illegal: method must have non-blank name
}

 

 둘 이상의 타입으 인터페이스를 구현할 수 있다. 예를 들어, 두 가지 타입 S1과 S2에 메소드 집합이 있는 경우

func (p T) Read(p []byte) (n int, err error)
func (p T) Write(p []byte) (n int, err error)
func (p T) Close() error

 

(여기서 T는 S1 또는 S2를 나타냄) 파일 인터페이스는 S1과 S2가 갖고 있거나 공유할 수 있는 다른 메소드에 관계없이 S1과 S2 모두에 의해 구현된다.

 

 인터페이스 타입 집합의 멤버인 모든 타입은 해당 인터페이스를 구현한다. 특정 타입은 여러 가지 고유한 인터페이스를 구현할 수 있다. 예를 들어 모든 타입은 모든(인터페이스가 아닌) 타입 집합을 나타내는 빈 인터페이스를 구현할 수 있다.

interface{}

 

 편의상 미리 선언된 any 유형은 빈 인터페이스의 별칭이다. 마찬가지로 Locker라는 인터페이스를 정의하기 위해 타입 선언 내에 나타나는 다음 인터페이스 사양을 고려해보면 좋다.

type Locker interface {
	Lock()
	Unlock()
}

 

 만약 S1과 S2도 구현하는 경우,

func (p T) Lock() { … }
func (p T) Unlock() { … }

 

 

 그들은 Locker 인터페이스와 File 인터페이스를 구현한다.


Embedded Interfaces (내장 인터페이스)

 

 좀 더 일반적인 형태로 인터페이스 T는 인터페이스 타입 이름 E를 인터페이스 요소로 사용할 수 있다. 이를 T의 임베딩 인터페이스 E라고 한다. T의 타입 집합은 T에 명시적으로 선언된 메서드에 의해 정의된 타입 집합과 T의 임베디드 인터페이스 타입 집합의 교차점이다. 즉, T의 타입 집합은 명시적으로 선언된 T의 모든 메서드와 E의 모든 메서드를 구현하는 모든 타입의 집합이다.

type Reader interface {
	Read(p []byte) (n int, err error)
	Close() error
}

type Writer interface {
	Write(p []byte) (n int, err error)
	Close() error
}

// ReadWriter's methods are Read, Write, and Close.
type ReadWriter interface {
	Reader  // includes methods of Reader in ReadWriter's method set
	Writer  // includes methods of Writer in ReadWriter's method set
}

 

 인터페이스를 포함할 때 동일한 이름을 가진 메서드에는 동일한 시그니처가 있어야 한다.

type ReadCloser interface {
	Reader   // includes methods of Reader in ReadCloser's method set
	Close()  // illegal: signatures of Reader.Close and Close are different
}

 General Interfaces (일반 인터페이스)

 

 가장 일반적인 형태에서 인터페이스 요소는 임의의 타입 용어 T일 수도 있고 기본 타입 T를 지정하는 ~T 형식의 용어이거나 용어 t1|t2|…|tn의 합집합일 수도 있다. 메소드 사양과 함께 이러한 요소를 사용하면 다음과 같이 인터페이스 타입 집합을 정확하게 정의할 수 있다.

  • 빈 인터페이스 타입 집합은 인터페이스가 아닌 모든 타입의 집합이다.
  • 비어 있지 않은 인터페이스의 타입 집합은 해당 인터페이스 요소의 타입 집합이 교차하는 것이다.
  • 메소드 사양의 타입 집합은 메소드 세트에 해당 메소드가 포함된 모든 비인터페이스 타입의 집합이다.
  • 비인터페이스 타입 용어의 타입 집합은 해당 타입만으로 구성된 집합이다.
  • ~T 타입의 용어 타입 집합은 기본 타입이 T인 모든 타입의 집합이다.
  • 용어 통합의 타입 집합 t1|t2|…|tn은 용어 타입 집합의 통합이다.

 "모든 비인터페이스 타입의 집합"이라는 것은 현재 프로그램에서 선언된 모든(비인터페이스) 타입뿐만 아니라 가능한 모든 프로그램에서 가능한 모든 타입을 의미하므로 무한하다. 마찬가지로, 특정 메소드를 구현하는 모든 비인터페이스 타입 집합이 주어지면 해당 타입의 메소드 집합 교차에는 프로그램의 모든 타입이 항상 해당 메소드를 다른 메소드와 쌍을 이루더라도 정확히 해당 메소드가 포함된다.

 

 구성에 따라 인터페이스 타입 집합에는 인터페이스 타입이 포함되지 않는다.

// An interface representing only the type int.
interface {
	int
}

// An interface representing all types with underlying type int.
interface {
	~int
}

// An interface representing all types with underlying type int that implement the String method.
interface {
	~int
	String() string
}

// An interface representing an empty type set: there is no type that is both an int and a string.
interface {
	int
	string
}

 

 ~T 타입의 용어에서 T의 기본 타입은 그 자체여야 하며 T는 인터페이스가 될 수 없다.

type MyInt int

interface {
	~[]byte  // the underlying type of []byte is itself
	~MyInt   // illegal: the underlying type of MyInt is not MyInt
	~error   // illegal: error is an interface
}

 

 유니온 요소는 타입 집합의 공용체를 나타낸다.

// The Float interface represents all floating-point types
// (including any named types whose underlying types are
// either float32 or float64).
type Float interface {
	~float32 | ~float64
}

 

 T 또는 ~T 타입의 용어에 있는 타입 T는 타입 매개변수가 될 수 없으며 모든 비인터페이스 용어의 타입 집합은 쌍으로 분리 되어야 한다(타입 집합의 쌍으로 교차하는 부분은 비어 있어야 함). 타입 매개변수 P가 주어질 때에:

interface {
	P                // illegal: P is a type parameter
	int | ~P         // illegal: P is a type parameter
	~int | MyInt     // illegal: the type sets for ~int and MyInt are not disjoint (~int includes MyInt)
	float32 | Float  // overlapping type sets but Float is an interface
}

 

 구현 제한: 둘 이상의 용어가 있는 공용체에는 미리 선언된 비교 가능한 식별자 또는 메서드를 지정하는 인터페이스를 포함할 수 없으며, 메서드를 지정하는 비교 가능한 인터페이스 또는 인터페이스를 포함할 수 없다.

 

 기본이 아닌 인터페이스는 타입 제약 조건으로만 사용되거나 제약 조건으로 사용되는 다른 인터페이스의 요소로만 사용될 수 있다. 값이나 변수 타입이거나 인터페이스가 아닌 다른 타입의 구성요소일 수 없다.

var x Float                     // illegal: Float is not a basic interface

var x interface{} = Float(nil)  // illegal

type Floatish struct {
	f Float                 // illegal
}

 

 인터페이스 타입 T는 T를 직접적으로 또는 간접적으로 포함하거나 포함하는 타입 요소를 포함할 수 없다.

// illegal: Bad may not embed itself
type Bad interface {
	Bad
}

// illegal: Bad1 may not embed itself using Bad2
type Bad1 interface {
	Bad2
}
type Bad2 interface {
	Bad1
}

// illegal: Bad3 may not embed a union containing Bad3
type Bad3 interface {
	~int | ~string | Bad3
}

// illegal: Bad4 may not embed an array containing Bad4 as element type
type Bad4 interface {
	[10]Bad4
}

인터페이스 구현

 T 타입은 다음과 같은 경우 인터페이스 I를 구현한다.

  • T는 인터페이스가 아니며 I 타입 집합의 요소이다.
  • T는 인터페이스이고 T의 타입 집합은 I 타입 집합의 하위 집합이다.

 T가 인터페이스를 구현하는 경우 T 타입의 값은 인터페이스를 구현한다.


Map types

 맵은 요소 타입이라고 하는 한 타입의 요소를 정렬되지 않은 그룹으로, 키 타입이라고 하는 다른 타입의 고유 키 집합으로 인덱싱된다. 초기화되지 않은 맵의 값은 nil이다.

MapType     = "map" "[" KeyType "]" ElementType .
KeyType     = Type .

 

 비교 연산자 == 및 !=는 키 타입의 피연산자에 대해 완전히 정의되어야 한다. 따라서 키 타입은 함수, 맵 또는 슬라이스가 아니어야 한다. 키 타입이 인터페이스 타입인 경우 동적 키 값에 대해 이러한 비교 연산자를 정의해야 한다. 정의하지 않으면 런타임 에러가 발생한다.

map[string]int
map[*T]struct{ x, y float64 }
map[string]interface{}

 

 맵 요소의 수를 길이라고 한다. 맵 m의 경우 내장 함수 len을 사용하여 검색할 수 있으며 실행 중에 변경될 수 있다. 할당을 사용하여 실행 중에 요소를 추가하고 인덱스 표현식을 사용하여 검색할 수 있다. 내장된 삭제 및 지우기 기능을 사용하여 제거할 수도 있다.

 

 새로운 빈 맵 값은 내장 함수 make를 사용하여 생성된다. 이 함수는 맵 타입과 선택적 용량 힌트를 인수로 사용한다.

make(map[string]int)
make(map[string]int, 100)

 

 초기 용량은 크기에 제한을 두지 않는다. 맵은 nil 맵을 제외하고 맵에 저장된 항목 수를 수용할 수 있도록 동적으로 증가한다. nil 맵은 요소를 추가할 수 없다는 점을 제외하면 빈 맵과 동일하다.


Channel Types (채널 타입)

 채널은 지정된 요소 타입의 값을 보내고 받음으로써 통신하는 기능을 동시에 실행하기 위한 메커니즘을 제공한다. 초기화되지 않은 채널의 값은 nil이다.

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

 

 선택적 <- 연산자는 채널 방향(송신 또는 수신)을 지정한다. 방향이 지정되면 채널은 방향성이 있고, 그렇지 않으면 양방향이다. 채널은 할당이나 명시적 변환을 통해 전송하거나 수신만 하도록 제한될 수 있다.

chan T          // can be used to send and receive values of type T
chan<- float64  // can only be used to send float64s
<-chan int      // can only be used to receive ints

 

 <- 연산자는 가능한 가장 왼쪽의 채널과 연결된다.

chan<- chan int    // same as chan<- (chan int)
chan<- <-chan int  // same as chan<- (<-chan int)
<-chan <-chan int  // same as <-chan (<-chan int)
chan (<-chan int)

 

 새로 초기화된 채널 값은 채널 타입과 선택적 용량을 인수로 사용하는 내장 함수 make를 사용하여 만들 수 있다.

make(chan int, 100)

 

 용량(요소 수)은 채널의 버퍼 크기를 설정한다. 용량이 0이거나 없으면 채널은 버퍼링되지 않으며 송신자와 수신자가 모두 준비된 경우에만 통신이 성공한다. 그렇지 않으면 채널이 버퍼링되고 버퍼가 가득 차지 않았거나(전송) 비어 있지 않은 경우(수신) 차단 없이 통신이 성공한다. nil 채널은 통신할 준비가 되어 있지 않는 케이스이다.

 

 내장 함수인 close를 사용하여 채널을 닫을 수 있다. 수신 연산자의 다중 값 할당 형식은 채널이 닫히기 전에 수신된 값이 전송되었는지 여부를 보고한다.

 

 추가 동기화 없이 여러 고루틴에서 send 문, receive 작업, 내장 함수 cap 및 len 호출에 단일 채널을 사용할 수 있다. 채널은 선입선출(FIFO) 대기열 역할을 한다. 예를 들어, 한 고루틴이 채널에 값을 보내고 두 번째 고루틴이 이를 수신하는 경우 보낸 순서대로 값이 수신된다.

반응형

'Golang' 카테고리의 다른 글

Golang 입문기 - 문법 4  (2) 2024.02.11
Golang 입문기 - 문법 2  (2) 2023.10.27
Golang 입문기 - 문법 1  (2) 2023.10.24
Golang 입문기 - 0  (0) 2023.10.20