Interfaces
## Objetivos de Aprendizaje
- Comprender interfaces como contratos
- Implementar interfaces implícitamente
- Usar interface vacía
- Dominar aserciones y switches de tipo
- Diseñar con interfaces
## ¿Qué es una Interface?
Una interface define un contrato. Los tipos que implementan una interface deben proporcionar todos los métodos especificados.
## Declaración
### Interface Básica
```go
type Speaker interface {
Speak() string
}
```
### Múltiples Métodos
```go
type Animal interface {
Speak() string
Move() string
}
```
## Implementación
### Implementación Implícita
Los tipos implementan interfaces implícitamente - sin declaración explícita.
```go
type Dog struct {
Name string
}
func (d Dog) Speak() string {
return "¡Guau!"
}
func main() {
var s Speaker = Dog{Name: "Buddy"}
fmt.Println(s.Speak()) // ¡Guau!
}
```
### Ambas Implementaciones
```go
type Cat struct{}
func (c Cat) Speak() string {
return "¡Miau!"
}
type Cow struct{}
func (c Cow) Speak() string {
return "¡Muuu!"
}
func main() {
animals := []Speaker{Cat{}, Cow{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
```
## Interface Vacía
### interface{} (interface vacía)
```go
var i interface{} = "hello"
i = 42
i = true
```
### Acepta Cualquier Cosa
```go
func printAll(items ...interface{}) {
for _, item := range items {
fmt.Println(item)
}
}
printAll(1, "hello", true, 3.14)
```
## Aserciones de Tipo
### Aserción Básica
```go
var i interface{} = "hello"
s := i.(string) // Panics si no es string
fmt.Println(s) // hello
s, ok := i.(string) // Seguro: ok es false si no es string
fmt.Println(s, ok) // hello true
```
### Asertar Tipo Incorrecto
```go
var i interface{} = 42
s, ok := i.(string)
fmt.Println(s, ok) // "" false (sin panic)
```
### Panic en Fallo
```go
var i interface{} = 42
s := i.(string) // PANIC: conversión de interface fallida
```
## Type Switch
### switch en Tipo
```go
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Entero: %d\n", v)
case string:
fmt.Printf("Cadena: %s (longitud %d)\n", v, len(v))
case bool:
fmt.Printf("Booleano: %t\n", v)
case float64:
fmt.Printf("Float64: %.2f\n", v)
default:
fmt.Printf("Tipo desconocido: %T\n", v)
}
}
func main() {
describe(42)
describe("hello")
describe(true)
}
```
### Type Switch con Interface
```go
type Shape interface {
Area() float64
}
type Circle struct{ Radius float64 }
type Rectangle struct{ Width, Height float64 }
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func printArea(s Shape) {
switch shape := s.(type) {
case Circle:
fmt.Printf("Círculo con radio %.2f, área %.2f\n", shape.Radius, shape.Area())
case Rectangle:
fmt.Printf("Rectángulo %.2f x %.2f, área %.2f\n", shape.Width, shape.Height, shape.Area())
default:
fmt.Println("Forma desconocida")
}
}
```
## Valores de Interface
### Estructura
Un valor de interface tiene dos componentes:
1. Tipo dinámico
2. Valor dinámico
```go
var s Speaker
fmt.Printf("%+v\n", s) //
s = Dog{Name: "Buddy"}
fmt.Printf("%+v\n", s) // {Name:Buddy}
```
### Interface Nil
```go
var s Speaker = nil
if s == nil {
fmt.Println("La interface es nil")
}
```
### Interface con Valor Nil
```go
var s Speaker = (*Dog)(nil)
if s == nil {
fmt.Println("La interface es nil")
} else {
fmt.Println("La interface no es nil, pero el valor es nil")
}
```
## Errors
### Interface error
```go
type error interface {
Error() string
}
```
### Error Personalizado
```go
type DivisionError struct {
Dividend, Divisor float64
}
func (e *DivisionError) Error() string {
return fmt.Sprintf("no se puede dividir %.2f por cero", e.Dividend)
}
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, &DivisionError{Dividend: a, Divisor: b}
}
return a / b, nil
}
```
### Usando Errors
```go
result, err := divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Resultado:", result)
```
## io.Reader e io.Writer
### io.Reader
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
```
### io.Writer
```go
type Writer interface {
Write(p []byte) (n int, err error)
}
```
### Usando Readers
```go
import "io"
import "strings"
reader := strings.NewReader("Hello, World!")
buf := make([]byte, 5)
for {
n, err := reader.Read(buf)
if err == io.EOF {
break
}
fmt.Printf("Leídos %d bytes: %s\n", n, buf[:n])
}
```
### Usando Writers
```go
import "os"
file, err := os.Create("output.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
n, err := file.Write([]byte("Hello, World!"))
fmt.Printf("Escribidos %d bytes\n", n)
```
## Stringer
### fmt.Stringer
```go
type Stringer interface {
String() string
}
```
### Implementación de Stringer
```go
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30)
}
```
## Composición de Interfaces
### Incrustar Interfaces
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
type ReadCloser interface {
Reader
Closer
}
```
## Mejores Prácticas
### Aceptar Interfaces, Retornar Structs
```go
func NewService(repo Repository) Service {
return &service{repo: repo}
}
type Service interface {
DoSomething() error
}
```
### Mantener Interfaces Pequeñas
```go
// Interface pequeña (preferido)
type Writer interface {
Write([]byte) (int, error)
}
// Interface grande (evitar)
type Everything interface {
Read([]byte) (int, error)
Write([]byte) (int, error)
Close() error
// ...
}
```
## Verificación de Interface Nil
```go
func doSomething(i interface{}) {
if i == nil {
fmt.Println("interface nil")
return
}
fmt.Printf("Valor: %v, Tipo: %T\n", i, i)
}
```
## Resumen
- Las interfaces definen contratos (conjuntos de métodos)
- Los tipos implementan interfaces implícitamente
- La interface vacía `interface{}` acepta cualquier tipo
- Aserciones de tipo: `i.(T)` con idiom comma-ok
- Type switch: `switch v := i.(type)`
- Interface error: `Error() string`
- io.Reader e io.Writer son interfaces comunes
- Aceptar interfaces, retornar tipos concretos
- Mantener interfaces pequeñas y enfocadas
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →