Я пытаюсь реализовать интерфейс «абстрактно». Под этим я подразумеваю, что пытаюсь реализовать метод(ы) этого интерфейса с точки зрения методов другого интерфейса.
Вот что я пробовал (но это не работает, потому что нам не разрешено использовать тип интерфейса в качестве получателя):
package symbols
import "fmt"
type Symbol interface {
Name() string
}
type TerminalSymbol struct {
rune rune
}
func (t TerminalSymbol) Name() string {
return `'` + string(t.rune) + `'`
}
type NonTerminalSymbol struct {
name string
}
func (nt NonTerminalSymbol) Name() string {
return nt.name
}
/////////////////////////////////////////////////////////
/// Try to implement 'Stringer' interface for Symbols
func (s Symbol) String() string {
//^ ERROR: invalid receiver type Symbol (pointer or interface type)
return fmt.Sprintf("A symbol with name: %v", s.Name())
}
Я полагаю, компилятор go, по сути, говорит мне, что реализация одного интерфейса на основе другого интерфейса просто не поддерживается?
Я полагаю, я мог бы просто понять намек и просто сделать это вместо этого:
func (s NonTerminalSymbol) String() string {
return fmt.Sprintf("A symbol with name: %v", s.Name())
}
func (s TerminalSymbol) String() string {
return fmt.Sprintf("A symbol with name: %v", s.Name())
}
Это работает, но в итоге дублирует реализацию метода 'String'.
В Java мы можем легко избежать такого дублирования; например путем добавления конкретных методов в абстрактный класс; или добавив реализацию «по умолчанию» к некоторым методам в интерфейсе.
Есть ли «идиоматический» способ сделать это на ходу? (то есть можем ли мы сделать абстрактный тип, такой как Symbol
, реализовать Stringer
, не дублируя реализацию String()
.
У вас не может быть структуры, похожей на абстрактный базовый класс, который вызывает методы конкретной реализации. Есть способы подражать этому, но я предполагаю, что это не то, о чем вы спрашиваете.
В Go интерфейс — это просто список методов, и концепция наследования классов не существует. Вы можете создавать иерархии объектов, используя встраивание типов, но это не обеспечивает позднего связывания, необходимого для реализации этого.
Однако вы можете сделать это:
Создайте функцию стрингера для символов:
func SymbolToString(s Symbol) string {
return fmt.Sprintf("A symbol with name: %v", s.Name())
}
Теперь вы можете написать простую реализацию метода String
для каждой структуры:
type NonTerminalSymbol struct {
name string
}
func (n NonTerminalSymbol) String() string {return SymbolToString(n)}
Простой ответ заключается в том, что получатель метода не знает, встроен ли он в другую структуру. Внедрение типов — это единственный способ составления наборов методов, и у метода нет информации о времени выполнения, чтобы решить, во что он встроен.
Вы можете делать «абстрактные» реализации, которые не требуют поздней привязки, хотя ваш пример не такой.
Спасибо, я думаю, вы подтвердили то, что я думал (я просто не был уверен, что это действительно невозможно, я просто подозревал это). Я нахожу это несколько удивительным и хотел бы понять логику этого. Но я полагаю, что это действительно другой вопрос. Поэтому я приму этот ответ и, возможно, подумаю о создании еще одного вопроса, который спрашивает «почему».