文档章节

Go语言中隐式接口的冲突问题

chai2010
 chai2010
发布于 2015/05/19 13:32
字数 691
阅读 471
收藏 6

Go语言中隐式接口的冲突问题

Go语言中采用的是隐式接口, 只要满足的接口的定义, 就可以当作接口使用.

比如内置的 error 接口:

type error struct {
	Error() string
}

隐式接口的好处有很多. 但我个人觉得最主要的一点就是不需要再去画祖宗八代的继承关系图了(松耦合).

但是隐式接口会带来冲突问题.

简单来说, 我也想定义一个自己的 MyError 接口, 里面也有一个 Error() string 方法:

type MyError struct {
	Error() string
}

但是我希望 MyError 接口 和 error 接口 是不同的类型 (不能相互转换).

当然, 在 Go语言中 MyError 接口 和 error 接口 是等价的, 禁止 相互转换 比较困难.

我们一般可以在 MyError 接口 中增加一个唯一的空方法 来回避这个问题:

type MyError struct {
	Error() string
	AssertMyError()
}

方法 AssertMyError 只是为了区别 error 接口, 没有其他用处.

这是 Protobuf 中 proto.Message 采用的方法:

// Message is implemented by generated protocol buffer messages.
type Message interface {
	Reset()
	String() string
	ProtoMessage()
}

生成的每个 Message 类型有个特殊的 ProtoMessage 空方法, 特别对应 proto.Message 接口.

当然, 如果有另一个接口刚好也有 ProtoMessage 方法, 还是有冲突的危险.

极端的做法是随机生成一个 特别的 方法名, 比如用 UUID 做唯一名字.

但是, 公开的名字依然有被别人恶意覆盖的危险(实际中不大可能).

更严格的做法是将这个用于区别接口的方法名定义为私有的方法. 比如 testing.TB:

type TB interface {
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
	Fail()
	FailNow()
	Failed() bool
	Fatal(args ...interface{})
	Fatalf(format string, args ...interface{})
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Skip(args ...interface{})
	SkipNow()
	Skipf(format string, args ...interface{})
	Skipped() bool

	// A private method to prevent users implementing the
	// interface and so future additions to it will not
	// violate Go 1 compatibility.
	private()
}

private 不仅仅是私有方法, 而且必须是 testing 包内部定义的 private() 方法的类型才能匹配这个接口!

因此 testing.TB 接口是全局唯一的, 不会出现等价可互换的接口.

现在 testing.TB 保证了接口的唯一性, 但是如何在外部实现 这个接口呢(private()testing 包内部定义的)?

我们可以从 testing.TB 接口继承这个 private() 方法:

package main

import (
	"fmt"
	"testing"
)

type TB struct {
	testing.TB
}

func (p *TB) Fatal(args ...interface{}) {
	fmt.Println("TB.Fatal disabled!")
}

func main() {
	var tb testing.TB = new(TB)
	tb.Fatal("Hello, playground")
}

play 地址: http://play.golang.org/p/tFB0fLwq9q

上面的代码模拟了显式接口, 而且 testing.TB 接口永远不用担心有冲突的危险.

当然, 上面的代码有过度使用技巧的问题, 这和Go语言简单的编程哲学是矛盾的.

© 著作权归作者所有

共有 人打赏支持
chai2010

chai2010

粉丝 420
博文 101
码字总数 82162
作品 10
武汉
程序员
私信 提问
大数据(十一)--Scala编程语言-提高

版权声明:本文版权归宋坤所有,转载请注明出处 https://blog.csdn.net/skisqibao/article/details/83750021 Scala Scala字符串 Scala 集合 trait特性 模式匹配match-case 样例类case classe...

skisqibao
2018/11/15
0
0
AutoLayout 使用详解

前言 故事从一年前说起,当时由于接到一个新项目开发任务开发之前想了想以前项目布局方式大多数都是计算有的也用到。 大家都知道适配各种屏幕非常繁琐各种坐标计算代码很冗余后期难以维护。 ...

WHC
2018/10/30
0
0
C++雾中风景5:Explicit's better than implicit.聊聊Explicit.

关于Explicit还是Implicit一直是编程语言中能让程序员们干起架的争议。那些聪明的老鸟总是觉得Implicit的规则让他们能够一目十行,减少样板代码的羁绊。而很多时候,Implicit的很多规则会让新...

LeeHappen
2018/01/04
0
0
(翻译)Java SE 8 Lambda 特性与基本原理(下)

6 , 词法域(Lexical Scoping) 确定内部类中变量名字(包括this)的意义要比在顶级类中困难的多,并且很容易出错。继承成员--包括类对象中的方法--可能不小心就覆盖了外部类的声明, 未加限...

麦壳原野
2013/12/20
0
2
Scala笔记整理(八):类型参数(泛型)与隐士转换

[TOC] 概述 类型参数是什么?类型参数其实就是Java中的泛型。大家对Java中的泛型应该有所了解,比如我们有List list = new ArrayList(),接着list.add(1),没问题,list.add("2"),然后我们l...

xpleaf
2018/04/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Httpd 整合 Tomcat 步骤

环境:Tomcat8 + Httpd2.4 工作原理:借助于Tomcat的AJP连接器实现Apache与Tomcat的通信 配置步骤: 1. 配置httpd.conf 新增: Include conf/extra/mod_jk.conf 修改:添加 index.jsp <IfM...

ZeroneLove
昨天
1
0
Docker笔记3——容器命令(未写完,明天整理接着写)

未写完,明天整理接着写 新建并启动容器 docker run docker run [OPTIONS] IMAGE [COMMEND] [ARG...] OPTIONS: --name=[容器新名字] :为容器指定一个名称 -d:后台运行容器,并返回容器ID,...

HappyBKs
昨天
1
0
2018个人年终总结

感谢领导的信任和指导,新的一年获得了很多成长和提高,改掉了很多不好的习惯。 在这一年里,我在领导的帮助下,主要完成了以下功能: 1、完成上海银行版本投资营销相关功能的开发。 2、完成车...

万山红遍
昨天
9
0
保密工作与linux系统的发展

保密工作从性质上可以分成商业方面的保密和国家安全方面的保密。由于自己从事的是IT方面的工作,工作中必然会接触涉及到计算机信息方面的相关文件。加上单位已近通过武器装备科研生产单位二级...

linux-tao
昨天
2
0
Spark共享变量

概述 Spark程序的大部分操作都是RDD操作,通过传入函数给RDD操作函数来计算。这些函数在不同的节点上并发执行,但每个内部的变量有不同的作用域,不能相互访问,所以有时会不太方便,Spark提...

仟昭
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部