Статьи

Quick Go-lang для разработчиков Java

Golang, похоже, становится довольно популярным в качестве языка программирования для некоторых интересных новых технологий, таких как Docker , Kubernetes и OpenShift . К счастью, все они тоже с открытым исходным кодом, что означает, что мы все можем внести свой вклад в эти сообщества и принять участие. Очевидным преимуществом open source, которое оказывается чрезвычайно ценным и полезным во многих обстоятельствах, является очевидное: открытый исходный код . Я не могу сказать вам, как часто мне приходилось прыгать в источник проекта, независимо от языка программирования, используемого для его реализации, чтобы по-настоящему понять, что он делает, или даже что более важно: почему он не делает что-то, что, по словам доктора, должно (или когда нет документов). Код читается гораздо чаще, чем написано, поэтому он помогает понять, как его читать.

В свое время я выучил немало языков программирования (C, C ++, C #, Java, Python, Scala, Groovy, Assembly, JavaScript и т. Д.), И теперь это Go. Я потратил несколько недель, чтобы научиться читать Golang и читать исходные файлы из кодовых баз Kubernetes, Fabric8 и OpenShift Origin, чтобы понять их немного глубже, и я думаю, что это будет полезно для разработчиков, знакомых с другим языком программирования. быстро сопоставить некоторые концепции, скажем, с Java, с Голангом с простой целью преодоления страха вступления, чтобы помочь этим сообществам или, более эгоистично, прочитать и понять, как некоторые из новых технологий написаны на Голанге. на самом деле работает.

Вот несколько ключевых моментов, которые поначалу выделялись для меня. Не стесняйтесь, чтобы Tweet (@christianposta) меня другие, которые вы чувствуете, могут быть добавлены здесь, или оставить комментарий. Эта статья определенно не предназначена для введения в Go и не является полным Java-> Go-отображением. Цель этой статьи — быстро помочь разработчикам Java понять несколько распространенных идиом Go, с точки зрения того, как это соотносится с Java.

Структура проекта

Похоже, что общепринятым соглашением для проектов Golang является размещение всех двоичных исходных файлов cli или файлов в «основном» пакете в папке cmd которая находится в корне дерева исходного кода. Пакеты, которые реализуют множество функциональных возможностей и представляют собой упорядоченные наборы типов, констант, переменных и функций, обычно можно найти в папке pkg в корне исходного каталога.

пакеты

Golang организует свой код в пакеты, аналогично Java. Вы объявляете, в каком пакете находится исходный файл (и все его константы, типы, функции и т. Д.), Вводя package <package_name> в верхней части исходного файла; однако, в отличие от Java, вы не указываете полный путь (местоположение каталога), только имя. Пример:

1
package api

Поэтому, если у вас есть пакет «api / endpoints», у вас будет такая структура каталогов в файловой системе (например, ./pkg/api/endpoints), но пакет endpoints будет объявлен в исходных файлах в каталоге endpoints , но они будут выглядеть так:

1
package endpoints

Импорт пакетов

Мы можем импортировать пакеты, как в Java, с помощью следующего:

1
2
3
4
5
6
7
import (
    stderrs "errors"
    "time"
 
    "golang.org/x/net/context"
    "k8s.io/kubernetes/pkg/auth/user"
)

Мы можем использовать пакеты в исходном коде, основываясь на последнем имени пакета в пути пакета. Например, в приведенном выше примере мы импортируем k8s.io/kubernetes/pkg/auth/user и из кода мы можем ссылаться на элементы внутри этого пакета с помощью user.Foo() . Если мы хотим переименовать пакет в нашем исходном файле, чтобы он не конфликтовал с другими именами, мы можем сделать то, что сделали выше:

1
2
3
import (
    stderrs "errors"
)

И обратитесь к пакету errors в нашем исходном коде как stderrs.Foo()

Основной пакет

main пакет — это то, где Голанг ищет точку входа в приложение. main пакет должен иметь функцию main() которая не принимает аргументов и не возвращает значений:

1
func main() { … }

Как уже упоминалось, этот пакет обычно находится в папке cmd в корневом каталоге.

Область / видимость типов, констант, функций

В golang, чтобы обеспечить область видимости для структуры / типа / функции / переменной, которая должна быть представлена ​​вне пакета, первый символ в символе имеет значение. Например, в пакете foo , если у вас есть функция с именем func Bar() , то, поскольку «Bar» имеет первую букву в верхнем регистре, она будет использоваться вне пакета. Если вы импортируете пакет foo , вы сможете получить доступ к foo.Bar() . Если бы «бар» был в нижнем регистре, он был бы скрыт — то есть регистр первой буквы определяет видимость

Методы могут возвращать несколько значений

Функция или метод в golang (есть различие) может возвращать «кортеж» или возвращение нескольких значений. Например, вызов функции, которая возвращает несколько значений, выглядит следующим образом:

1
internalCtx, ok := foo.bar(context.Context)

Классы, структуры, методы

В Java у нас есть классы, но в Go у нас есть структуры. Структуры могут иметь методы, поэтому они как классы.

Например:

1
2
3
4
type Rectangle struct {
    width int
    height int
}

Это структура данных с именем «Прямоугольник», которая имеет два поля: width и height . Мы можем создать новый Rectangle так:

1
r := new(Rectangle)

и мы можем ссылаться на его поля, как это:

1
2
r.width = 10
r.height = 5

Мы можем написать методы на Struct, который работает с полями структуры, например так:

1
2
3
func (r *Rectangle) area() int {
    return r.width * r.height
}

Поэтому, если вы видите такие идиомы в коде golang, подумайте «классы Java»

Тип наследования

Golang целенаправленно не имеет типа «extends», как в Java. Наследование осуществляется с помощью композиции (вроде как мы должны делать это в Java как «лучшую практику» в любом случае :)). Однако, это действительно похоже на ключевое слово «extends» в Java с точки зрения его видимости для программиста. Например:

1
2
3
4
5
type Rectangle struct {
    Shape
    width int
    height int
}

В этом примере Rectangle имеет анонимное поле типа Shape . Все поля и методы Shape будут видны на объектах Rectangle .

Однако следует отметить, что, поскольку Shape Rectangle «is-a» не похожа на Java, где мы можем передать Rectangle функции, которая принимает Rectangle в качестве параметра. Это потерпит неудачу в Go. Чтобы получить этот тип «типового» полиморфизма, вы должны использовать интерфейсы Go (следующий раздел)

Полиморфизм, интерфейсы и типирование утки

В Java у нас есть определенные типы интерфейсов, которые определяют поведение на основе методов, которые должны быть реализованы классами, которые претендуют на этот тип. В Go у нас действительно есть интерфейсы, которые делают то же самое, но классы на самом деле не объявляют, что собираются реализовать их специально: они просто реализуют методы, а затем они соответствуют этому интерфейсу.

Например, этот интерфейс объявляет тип Shape с методом Print ():

1
2
3
type Shape interface {
    Print()
}

Однако, когда мы создаем наши Structs, нам вообще не нужно объявлять это с помощью «реализаций», как мы делаем в Java: мы просто реализуем методы, и они могут передаваться функциям и обрабатываться как этот тип:

1
2
3
4
5
6
7
8
type Rectangle struct {
    width int
    height int
}
 
func (r *Rectangle) Print() {
    fmt.println("Rectangle!");
}

В этом случае объект Rectangle может быть передан функциям, которые ожидают тип Shape поскольку он реализует все методы для этого типа.

Для петель

Пример цикла for с go:

1
2
3
for i := 1; i <= 10; i++ {
  fmt.Println(i)
}

Однако при итерации массива (или чего-то, что выглядит как массив, например, строка, карта, срез и т. Д.), Вы можете использовать оператор диапазона следующим образом (предположим, что foo — это список):

1
2
3
for v := range foo {
  fmt.println("value="+v);
}

Если вам нужно знать индекс списка во время его итерации, он выглядит следующим образом:

1
2
3
for i, v := range foo {
  fmt.println("index " + i + "has value="+v);
}

Пока петли

В Go нет циклов while, do-while, foreach и т. Д. Вы просто снова используете цикл for следующим образом:

1
2
3
4
5
sum := 1
for sum < 1000 {
    sum += sum
}
fmt.Println(sum)

Или можете сделать бесконечный цикл while:

1
2
3
for {
    something...
}

Указатели и ссылки

Голанг явно использует указатели и ссылки, тогда как Java в основном это скрывает. Например, в Java мы могли бы сделать

Shape shape = new Shape() и затем shape.foo() но в Go мы должны напрямую позаботиться об указателях:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
type Rectangle struct {
    width int
    height int
}
 
func updateRectangle(r *Rectangle){
     
    r.width = 5;
    r.height = 10;
}
 
func main() {
    r := Rectangle{20,30}
    updateRectangle(&r)
}

Когда основная функция заканчивается, прямоугольник r будет иметь значения width = 5 и height = 10, как и следовало ожидать. Обратите внимание, мы должны сделать явные ссылки на указатели.

Вывоз мусора

Голанг — это язык для сбора мусора; нет необходимости явно освобождать память самостоятельно (я знаю, что вы думали, когда видели раздел указателей и ссылок выше :)).

Если вы являетесь Java-разработчиком и хотели бы добавить больше к этому, пожалуйста, дайте мне знать (@christianposta)! Или, если вы являетесь разработчиком, и я что-то здесь неверно прошу, поправьте меня! Надеюсь, что это полезно …

Ссылка: Quick Go-lang для разработчиков Java от нашего партнера JCG Кристиана Поста в блоге