<p>I'm porting a Java library to Go for work and we have a few<code>static final</code> variables in one of the classes. I know you can't do <code>const</code>structs in Go but I'm at a loss how to do this.</p>
<p>Here is the java line:</p>
<pre><code>public static final MyClass AWESOME = new MyClass("Awesome");
</code></pre>
<p>I thought of creating a function like:</p>
<pre><code>func Awesome() *MyClass {
return &MyClass{"Awesome"}
}
</code></pre>
<p>But that doesn't stop users from changing what the pointer is point to. I would like to use a pointer just so I only need to create on instance of the struct but maybe it's not possible.</p>
<p>Any ideas how to mimic this in Go?</p>
<hr/>**评论:**<br/><br/>jerf: <pre><p>Your <code>Awesome</code> function would create an instance of MyClass per call anyhow.</p>
<p>I've settled on a top-level "var" declaration with documentation that says "Don't change this". There's only so much you can do.</p></pre>Destructicorn: <pre><p>Yeah, I did some Googling around after I posted this and exactly what you said was the closest I saw.</p></pre>mcandre: <pre><p>The public/private casing rule in Go can be leveraged to construct factory patterns: Mark the struct as private, and offer a public method that generates instances</p></pre>jerf: <pre><p>That's mostly orthogonal to this question. If you do</p>
<pre><code>package sample
var SupposedToBeConst = factoryFunction()
</code></pre>
<p>then other code can still do <code>sample.SupposedToBeConst = SomethingElse</code> if the type is public, which is a reasonable possibility. Making that type private has its own problems, because while Go does permit you to expose values that have unexported types, they are difficult to use. And the decision about whether to export the type or not is not one that you want to be making based on whether or not you've got an exported "constant" you want to stay constant.</p>
<p>So, in conclusion, I've settled on a top-level "var" declaration with documentation that says "Don't change this". I actually fiddled with quite a lot of possibilities before that, but they all come with pretty nasty side effects vs. just trusting people not to touch the variables.</p>
<p>Also, people are generally <em>really</em> incentivized to not touch those variables because if you don't provide a sync.Mutex or something to synchronize those changes, they're unsafe to change anyhow because it's automatically a race condition once the the program gets past initialization. Granted, that's not the strongest protection in the world, but it's definitely a good reason to leave those variables alone unless something is explicitly documented about why it's OK to change them.</p></pre>Killing_Spark: <pre><p>Also, i mean if they change it and it breaks its kinda their own fault. You warned them not to do it. </p></pre>SYN_SYNACK_ACK: <pre><p>To prevent someone from creating multiple instances I recently started using this pattern</p>
<pre><code>import (
"sync"
)
var once sync.Once
var awesome *MyClass
type MyClass struct {
string
}
func Awesome() *MyClass {
once.Do(func() {
awesome = &MyClass{"Awesome"}
})
return awesome
}
</code></pre>
<p><a href="https://play.golang.org/p/yNoKLjoUXv" rel="nofollow">https://play.golang.org/p/yNoKLjoUXv</a></p></pre>tv64738: <pre><p>That's a lot of code for what amounts to</p>
<pre><code>var Awesome = &MyClass{"Awesome"}
</code></pre></pre>Destructicorn: <pre><p>I've recently been using that pattern also for Singletons in Go. I could use that but feels like it would be a lot of boilerplate to get what I'm after, but that doesn't mean it's wrong. A lot of good ideas on this thread trying to figure out which one works best for our situation.</p></pre>earthboundkid: <pre><p>If you just port a library from Java to Go and keep the API the same, it won't be Go. You can do a one-to-one as a first step, but really, you should rethink the API as part of the complete move.</p></pre>Destructicorn: <pre><p>We're keeping the Java library along side the new Go one. I've already redesigned some stuff maybe this requires a closer look.</p></pre>dinkumator: <pre><p>Plenty of examples in the stdlib. For example, the DefaultClient and DefaultServeMux in net/http: <a href="https://golang.org/pkg/net/http/#pkg-variables" rel="nofollow">https://golang.org/pkg/net/http/#pkg-variables</a></p>
<p>Generally, if it's an interface and the implementation details should stay hidden, leave the type unexported, otherwise a default+accessible export type are better.</p></pre>SportingSnow21: <pre><p>Don't use an exported type. The compiler won't allow users to change it without hacking the library.</p></pre>epiris: <pre><p>Can't help if you say you want a static final java object. Instead describe the object itself, what a user can do to it. Then you can get the most idiomatic suggestion. What others posted here is reasonable, but you can in fact get the identical behavior as this. Just because you return a value doesn't mean you can't grant access to mutate or read the state that you want. Go strings and slices are a good example that I imagine will click right away what I'm heading towards.</p>
<pre><code>type state struct { privateFields string; ... }
type MyClass struct { s *state }
// now my class has methods to mutate / read state but
// pass by Value only so no one can change what Awesome
// returns. It's cheap as passing a pointer around so no real
// penalties.
var awesome = &state{}
func Awesome() { return MyClass{awesome} }
// If you wanted to really go full anti pattern and
// write something I could only describe as Gova
// you could...
type MyClass string
const (
Awesome MyClass = "Awesome"
)
func (s MyClass) SuchFinal(muchPrivate string) {
govaMap[s].privateStuff = muchPrivate
}
var govaMap = map[MyClass]*state{
Awesome: &state{...},
}
</code></pre>
<p>The reflect pkg Value T is a good example too, it always returns a value with three words that point to internal types. It still has a full (one of the largest in the std library in fact) API that mutates internal state. Still though, in the spirit of Go try to justify complexity, ask yourself: would i assign to a global variable "by mistake". Other developers will have the same answer.</p></pre>paul2048: <pre><p>There are no final variables in Go. So there is no point trying to mimic final variables.</p></pre>egonelbre: <pre><p>There's an important piece missing. Why do you want to do all of this in the first place?</p></pre>Destructicorn: <pre><p>There overall goal is to port the Java library to Go in such a way that using it feels almost identical to the Java library. Implementation on the back end will be different to play more to Go's strengths. For this particular problem I wasn't sure in Go how to get the same feel for using that particular class as you would in Java. </p>
<p>I've resolved to starting to break down the Java convention and just do what feels right for Go.</p></pre>tv64738: <pre><blockquote>
<p>port the Java library to Go in such a way that using it feels almost identical to the Java library.</p>
</blockquote>
<p>That's a bad idea. And replace Java and Go with any two programming languages and it's still a bad idea.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传