Go语言中可以使用泛型<T extends Object>吗? [已关闭]

问题描述 投票:0回答:3

我在 Typescript 中有一个代码,它使用通用存储库,与以下示例类似。 ts游乐场

// Model class that have generic ID type
class Model<T = number | string> {
    id?: T;
    createdAt?: Date;
    updatedAt?: Date;
}

interface CrudRepository<T extends Model<ID>, ID = number | string> {
    getAll(): Array<T>
    findByID(id: ID): T
}

// GenericRepository class with generic model type, that have method for interact with DB (CRUD)
class GenericRepository<T extends Model<ID>, ID = number | string> implements CrudRepository<T, ID> {
    // dbContext
    constructor(mockData: Array<T>) {
        this.mockData = mockData
    }

    mockData: Array<T>

    getAll = (): Array<T> => this.mockData

    findByID = (id: ID): T => this.mockData.find((v) => v.id = id) || ({} as T) // Need to get ID values
}

// User model
class User extends Model<number> {
    name?: string

    constructor(id: number, name: string) {
        super();
        this.id = id;
        this.name = name;
    }
}

// User service 
class UserService {
    repo: CrudRepository<User, number>

    constructor(repo: GenericRepository<User, number>) {
        this.repo = repo
    }

    getAll = (): User[] => this.repo.getAll();
    findByID = (id: number): User => this.repo.findByID(id);
}

function main() {
    const mockUsers = [new User(1, 'Person A'), new User(2, 'Person B')];
    const repo = new GenericRepository(mockUsers);
    const serv = new UserService(repo);

    console.log(`All: ${JSON.stringify(serv.getAll())}`);
    console.log(`User 1: ${JSON.stringify(serv.findByID(1))}`);
}

main();

结果应该是:

[LOG]: "All: [{"id":1,"name":"Person A"},{"id":2,"name":"Person B"}]" 
[LOG]: "User 1: {"id":1,"name":"Person A"}" 

目前我的新项目想使用Go语言。据我所知,我们可以在 Go 中使用

T ~struct{}
,如下代码所示。 https://goplay.tools/snippet/Mk7A8JByRQA

package main

import (
    "fmt"
    "time"
)

func main() {
    repo := GenericRepository[User, int64]{
        mockData: mockUsers,
    }

    users := repo.GetAll()
    fmt.Println(users[0].ID)
    fmt.Println(users)
}

type Model[TID int64 | string] struct {
    ID        TID
    CreatedAt time.Time
    UpdateAt  time.Time
}

type User struct {
    Model[int64]
    // Name string // Remove it to run without error
}

type GenericRepository[T ~struct{ Model[ID] }, ID int64 | string] struct {
    mockData []T
}

func (r GenericRepository[T, ID]) GetAll() []T {
    return r.mockData
}

func (r GenericRepository[T, ID]) FindByID(id ID) T {
    // Error v.ID undefined (type T has no field or method ID)
    // for _, v := range r.mockData {
    //     if v.ID == id {
    //         return v
    //     }
    // }

    // Error v.Model undefined (type T has no field or method Model)
    // for _, v := range r.mockData {
    //  if v.Model.ID == id {
    //      return v
    //  }
    // }

    return *new(T)
}

var mockUsers = []User{
    User{Model: Model[int64]{ID: 1}},
    User{Model: Model[int64]{ID: 2}},
}

但是当我向 User struct

User does not satisfy ~struct{Model[int64]} (User missing in ~struct{main.Model[int64]})
添加更多字段时出现错误,并且我也无法与 T 内的值交互,例如 ID 或 Model。

type User struct {
    Model[int64]
    Name string
}
go
3个回答
0
投票

您的目标似乎是编写一个与 Person 和 Developer 值一起使用的 Greet 函数。

为了实现目标,请声明一个具有 Greet 所需功能的接口。 Greet 函数需要有名称的东西:

type Namer interface {
    Name() string
}

使用该界面编写问候语。

func Greet(a, b Namer) {
    fmt.Println(a.Name(), "Hello", b.Name())
}

定义满足接口的Person类型:

type Person struct {
    name string
}

func (p *Person) Name() string { return p.name }

func NewPerson(name string) *Person { return &Person{name: name} }

使用 embedding 创建具有人员的开发人员。 Developer 类型满足 Namer 接口,因为嵌入的 Person 字段上的 Name 方法被提升为 Developer 类型。

type Developer struct {
    *Person
    skill string
}

func NewDeveloper(name, skill string) *Developer {
    return &Developer{Person: NewPerson(name), skill: skill}
}

创建一些人员和开发者值并让他们打招呼:

a := NewPerson("Person A")
b := NewPerson("Person B")
dev := NewDeveloper("The dev", "Fullstack")

Greet(a, b)
Greet(dev, a)

https://go.dev/play/p/O9YsLcGUZK0


0
投票

你不能。 Go 在设计上没有继承。

Dylan Bourque 有一个很棒的演讲比我更好地解释了它,但基本上嵌入不是继承 - 虽然你可以使用嵌入结构的方法和字段,但你没有“is-a”关系,这在大多数支持继承的语言中都是有问题的。

你的函数有无用的泛型:

function Greet<T extends Person>(a: T, b: T) { console.log(a.name + " Hello " + b.name) }
因为如果你有继承,那有什么区别

function Greet(a: Person, b: Person) { console.log(a.name + " Hello " + b.name) }

通常,最好考虑一下你想要做什么,而不是尝试在问题中已经定义不明确的类型系统中表达某些内容。

如果您需要类似

<T extends Object>

之类的东西,也许
类型约束就是您正在寻找的东西 - 但从您的示例中很难说它首先不应该有泛型。

另请参阅 Go 常见问题解答中的

“Go 中的泛型与其他语言中的泛型相比如何?”


-1
投票
这可以通过声明 Greeter 接口来实现:

type Greeter interface { Name() string }
然后让 *Person 满足该接口:

func (p *Person) Name() string { return p.name }
其余部分是使用结构嵌入完成的:

https://go.dev/play/p/OD_ofnVw93t

© www.soinside.com 2019 - 2024. All rights reserved.