In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly explains "how to transplant Java code to Go". The content of the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "how to transplant Java code to Go".
Testing, code coverage
Automated testing and code coverage tracking can benefit large projects.
I used TravisCI and AppVeyor for testing. Codecov.io is used to detect code coverage. There are many other similar services.
I use both AppVeyor and TravisCI because Travis no longer supports Windows a year ago, and AppVeyor does not support Linux.
If I were to re-select these tools now, I would only use AppVeyor because it now supports testing on the Linux and Windows platforms, and the future of TravisCI is uncertain after it was acquired by a private equity firm and fired the original development team.
Codecov is hardly competent for code coverage testing. For Go, it treats non-code lines (such as comments) as unexecuted code. It is not possible to get 100% code coverage with this tool. Coveralls seems to have the same problem.
It's better than nothing, but these tools can make things better, especially for Go programs.
The race detection of Go is very good.
Part of the code uses concurrency, which is error-prone.
Go provides a race detector, which can be turned on using the-race field at compile time.
It slows down the program, but additional checks can detect whether the same memory location is being modified at the same time.
I have been turning on-race to run the test, and through its alarm, I can quickly fix those competition problems.
Build specific tools for testing
It is difficult to verify the correctness of large projects through naked eye inspection. There is so much code that it is difficult for your brain to remember it at once.
When a test fails, it is also a challenge to find the cause only from the information about the test failure.
The database client driver connects with the RavenDB database server using HTTP protocol, and the transmitted commands and responses are encoded by JSON.
When porting Java test code to Go, this information can be useful if you can get HTTP traffic from the Java client and server and compare it with the HTTP traffic generated by the code migrated to Go.
I built some specific tools to help me get the job done.
To get HTTP traffic from the Java client, I use Go to build a logging HTTP proxy that the Java client uses to interact with the server.
For the Go client, I built a hook that intercepts HTTP requests. I use it to record traffic in a file.
I can then compare the difference between the HTTP traffic generated by the Java client and the client migrated by Go.
The process of transplantation
You can't start migrating 50,000 lines of code at random. I am sure that if I do not test and verify after each small step, I will be defeated by the complexity of the overall code.
I am new to the RavenDB and Java codebase. So my step is to gain a deeper understanding of how this Java code works.
The core of the client is to interact with the server through HTTP protocol. I captured and studied the traffic and wrote the simplest Go code to interact with the server.
When this works, I am confident that I can replicate these functions.
My milestone is to migrate enough code to test by porting the simplest Java code.
I used a combination of bottom-up and top-down methods.
The bottom-up part means that I locate and migrate the code at the bottom of the call chain that sends commands to the server and parses the response.
The top-to-bottom part means that I step by step track the test code to be migrated to determine the functional code parts that need to be migrated.
After successfully completing the migration, all that is left is to migrate one test at a time, while porting all the required code that can pass the test.
When the test was ported and passed, I made some improvements to make the code more Go-style.
I believe that this step-by-step approach is very important to complete the migration.
From a psychological point of view, it is important to set short intermediate milestones when faced with a long-term project. The constant completion of these milestones gives me a lot of motivation.
It's also good to keep your code compilable, runnable, and testable all the time. When you finally have to face those cumulative defects, it will be difficult for you to solve them.
The challenge of porting Java to Go
The goal of the migration is to be as consistent as possible with the Java code base, because the migrated code needs to keep pace with future changes in Java.
Sometimes I'm amazed at the amount of code I migrate on a line-by-line basis. The most time-consuming part of the migration process is reversing the order in which variables are declared, with Java declaring type name and Go declaring name type. I sincerely hope that there are tools that can help me finish this part of the work.
String vs. String
In Java, a String is an object that is essentially a reference (pointer). Therefore, the string can be null.
String is a value type in Go. It can't be nil, just empty.
This is not a big deal, and in most cases I can mindlessly replace null with "".
Errors vs. Exceptions
Java uses exceptions to pass errors.
Go returns the value of the error interface.
Migration is not difficult, but a large number of function signatures need to be modified to support the return of error values and propagation on the call stack.
Generics
Go (currently) does not support generics.
Porting generic interfaces is a challenge.
Here is an example of a generic method in Java:
Public T load (Class clazz, String id) {
Caller:
Foo foo = load (Foo.class, "id")
In Go, I use two strategies.
One of these is to use interface {}, which consists of values and types, similar to object in Java. This method is not recommended. Although valid, it is not appropriate for users of this library to manipulate interface {}.
In some cases I can use reflection, and the above code can be ported to:
Func Load (result interface {}, id string) error
I can use reflection to get the type of result, and then create a value of that type from the JSON document.
The caller's code:
Var result * Foo err: = Load (& result, "id")
Function overload
Go does not support (and most likely will never support) function overloading.
I'm not sure if I've found the right way to port this code.
In some cases, overloading is used to create shorter helper functions:
Void foo (int a, String b) {} void foo (int a) {foo (a, null);}
Sometimes I just throw away shorter helpers.
Sometimes I write two functions:
Func foo (an int) {} func fooWithB (an int, b string) {}
Sometimes I do this when the number of potential parameters is large:
Type FooArgs struct {An int B string} func foo (args * FooArgs) {}
Inherit
Go is not an object-oriented language and has no inheritance.
Inheritance in simple cases can be migrated using a nested method.
Class B: A {}
Sometimes it can be migrated to:
Type A struct {} type B struct {A}
We embed An in B, so B inherits all A's methods and fields.
This method is not valid for virtual functions.
There is no good way to migrate code that uses virtual functions.
One way to simulate virtual functions is to nest structures and function pointers. In essence, this is a reimplementation of the virtual table provided by Java for free as part of the object implementation.
Another way is to write an independent function that schedules the correct function of a given type through type judgment.
Interface
Both Java and Go have interfaces, but they have different content, just like the difference between apples and pepperoni.
In rare cases, I do create interface types of Go to copy Java interfaces.
In most cases, I give up using interfaces and instead expose specific structures in API.
Circular introduction of dependent packages
Java allows a loop of packages to be introduced.
Go does not allow it.
As a result, I couldn't copy the package structure of the Java code in the migration.
To simplify, I use a package. This method is not ideal because the package will become very bloated. In fact, this package is so bloated that Go 1.10 cannot handle so many source files in a single package under Windows. Fortunately, Go 1.11 fixed this problem.
Private (private), public (public), protected (private)
The designers of Go are underestimated. Their ability to simplify concepts is powerful, and access control is an example.
Other languages tend to have fine-grained permission control: (the fields and methods of each class) specify the least possible granularity of public, private, and protection.
The result is that when external code uses the library, some of the functions implemented by the library have the same access rights as other classes in the library.
Go simplifies this concept by having only public and private access, with access limited to the package level.
It makes more sense.
When I want to write a library, for example, parsing markdown, I don't want to expose the internal implementation to the library's users. But for me to hide these internal implementations, the effect is just the opposite.
Java developers are aware of this problem and sometimes use interfaces as a technique for fixing overly leaked classes. By returning an interface rather than a concrete class, consumers of this class will not be able to see some of the available public interfaces.
Concurrence
To put it simply, the concurrency of Go is * *, and the built-in race detector is very helpful to solve the concurrency problem.
As I just said, the * port I performed is to simulate the Java interface. For example, I implemented a copy of the Java CompletableFuture class.
It is only after the code is ready to run that I reorganize the code to make it more Go-like.
Smooth function chain call
RavenDB has complex query capabilities. The Java client uses a chained approach to build queries:
List results = session.query (User.class) .groupBy ("name") .selectKey () .selectCount () .orderByDescending ("count") .ofType (ReduceResult.class) .toList ()
Chained calls are valid only in languages that make error interactions through exceptions. When a function returns an extra error, you cannot make a chained call as above.
To replicate chained calls in Go, I used a "stateful error" method:
Type Query struct {err error} func (Q * Query) WhereEquals (field string) Val interface {}) * Query {if q.err! = nil {return Q} / / logic that might set q.err return Q} func (Q * Query) GroupBy (field string) * Query {if q.err! = nil {return Q} / / logic that might set q.err return Q} func (Q * Query) Execute (result inteface {}) error {if q.err! = nil {return q.err} / / do logic}
Chain calls can be written as follows:
Var result * Foo err: = NewQuery () .WhereEquals ("Name", "Frank") .GroupBy ("Age") .Execute (& result)
JSON parsing
Java does not have a built-in JSON parsing function, and the client uses the Jackson JSON library.
Go has JSON support in the standard library, but it does not provide enough hook functions to show the process of JSON parsing.
I'm not trying to match all the Java features, because Go's built-in JSON support seems flexible enough.
Go code is shorter
Brevity is not an attribute of Java, but an attribute of a culture that writes code that conforms to language conventions.
The setter and getter methods are common in Java. For example, the Java code:
Class Foo {private int bar; public void setBar (int bar) {this.bar = bar;} public int getBar () {return this.bar;}}
The Go language version is as follows:
Type Foo struct {Bar int}
3 lines vs 11 lines. When you have a large number of classes and a lot of members in the class, you can accumulate these classes continuously.
Most other codes are about the same length.
Use Notion to organize your work
I am a heavy user of Notion.so. In the simplest terms, Notion is a multi-level note-taking application. Think of it as a combination of Evernote and wiki, carefully designed and implemented by software designers.
Here's how I use Notion to organize Go migration:
Here are the details:
I have a page with a calendar view not shown above, which is used to record short notes on work and time spent at a particular time. Because this contract is charged by the hour, so the statistics of working hours is very important information. Thanks to these notes, I know that I have spent 601 hours on this development in 11 months.
Clients like to keep abreast of progress. I have a page that records a monthly summary of my work, as follows:
These pages are shared with customers.
Short-term todo list is useful when starting your daily work.
I even use the Notion page to manage invoices and use the "Export as PDF" feature to generate the PDF version of the invoice.
Go programmers to be recruited
Does your company still need Go developers? You can hire me.
Additional resources
To address the problem, I have provided some additional instructions:
Hacker News discussion / r/golang discussion thank you for reading, the above is "how to transplant Java code to Go" content, after the study of this article, I believe you have a deeper understanding of how to transplant Java code to Go, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.