You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言:接口底层对象赋值不生效,如何正确修改其属性值

问题:接口底层结构体赋值后始终保留旧值

尝试通过接口修改其底层结构体的字段值,但修改后原结构体依然保留旧值,示例代码如下:

package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
    Name   string
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    Length float64
    Width  float64
    Name   string
}

func (r Rectangle) Area() float64 {
    return r.Length * r.Width
}

func assignRadius(s Shape, radius float64, name string) {
    switch s := s.(type) {
    case Circle:
        s.Radius = radius
        s.Name = name
    case Rectangle:
        s.Length = radius
        s.Name = name
    }
}

func main() {
    var s Shape
    c := Circle{Radius: 0, Name: "My Circle"}
    s = c
    fmt.Println(s.Area())
    fmt.Println(c.Radius)
    fmt.Println(c.Name)
    assignRadius(s, 10, "My New Circle")
    fmt.Println(c.Radius)
    fmt.Println(c.Name)
}

原因分析

问题核心是值拷贝:

  • 把Circle值直接赋值给接口变量s时,接口内部存储的是该结构体的副本,而非原对象的引用。
  • 在assignRadius函数中,类型断言case Circle:得到的也是结构体的临时副本,修改的只是这个副本的字段,原结构体完全不受影响。

解决方案

要修改原结构体,必须让接口持有结构体的指针,并且在类型断言时匹配指针类型:

  1. 将接口变量指向结构体指针:s = &c而非s = c
  2. 修改assignRadius中的类型断言分支,匹配指针类型*Circle和*Rectangle
  3. (可选但推荐)将结构体的方法改为指针接收者,避免不必要的值拷贝,同时保持接口实现的一致性

修改后的完整代码:

package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
    Name   string
}

// 改为指针接收者
func (c *Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

type Rectangle struct {
    Length float64
    Width  float64
    Name   string
}

// 改为指针接收者
func (r *Rectangle) Area() float64 {
    return r.Length * r.Width
}

func assignRadius(s Shape, radius float64, name string) {
    switch s := s.(type) {
    // 匹配指针类型
    case *Circle:
        s.Radius = radius
        s.Name = name
    case *Rectangle:
        s.Length = radius
        s.Name = name
    }
}

func main() {
    var s Shape
    c := Circle{Radius: 0, Name: "My Circle"}
    // 接口存储指针
    s = &c
    fmt.Println(s.Area())
    fmt.Println(c.Radius)
    fmt.Println(c.Name)
    assignRadius(s, 10, "My New Circle")
    fmt.Println(c.Radius) // 输出 10
    fmt.Println(c.Name)   // 输出 My New Circle
}

关键改动说明

  • 接口持有指针后,类型断言得到的是原结构体的指针,修改指针指向的字段会直接作用于原对象
  • 指针接收者的方法允许指针类型实现Shape接口,同时避免每次调用方法时拷贝结构体,性能更优

内容的提问来源于stack exchange,提问作者smm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 04:28:27