0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini に Go 言語で Java の ArrayListを作らせてみた (第二回 制約つきの構造体を追加してみた)

Last updated at Posted at 2025-07-16

なぜ Go 言語にコレクションクラスがないんだ!そうだ。 AI に作らせよう。

という訳で、Gemini だけで ArrayListGo で作らせてみました。

そのときの内容を6回に分けてお伝えしていこうと思います。

第二回 制約つきの構造体を追加してみた

分割して掲載する都合上、実際に質問を投げた順番と掲載の順番とを少々入れ替えてあります。

Geminiからは、毎回コード全体が出力されましたが、
記事が長くなりすぎてしまうため、変更があった部分だけをお送りいたします。

Contains メソッド

質問

contains メソッドはリストに指定した値があるかどうかを返すもの。
値が一致するかどうかを判断できるようにするには型制約をanyではなくcomparableにする必要がある。

ただ、MyArrayList構造体の制約をanyからcomparableに変更するだけだと、== で比較できない型はMyArrayListで使うことができなくなるので、
もうひとつ別の構造体をつくってくれないかなぁ。

以下のように聞いてみました。

MyArrayList[comparable] を持った構造体を作って contains メソッドを追加してください

「MyArrayList[comparable] を持った構造体」というのは「フィールド MyArrayList[comparable] を持っている構造体」のつもりでした。

なのに。。。

回答

承知いたしました。comparable 制約を持つMyArrayList構造体と、containsメソッドを追加します。

Goのジェネリクスにおけるcomparable制約は、その型が==!=演算子で比較可能であることを示します。これにより、containsメソッドで要素の比較を行うことができます。

以下に修正したコードを示します。

package main

import "fmt"

// MyArrayList はJavaのArrayListに似た機能を提供するジェネリックな動的配列です。
// comparable制約を追加し、要素が比較可能であることを示します。
type MyArrayList[T comparable] struct {
	elements []T
}

// Contains は指定された要素がリストに含まれている場合にtrueを返します。
func (list *MyArrayList[T]) Contains(element T) bool {
	for _, item := range list.elements {
		if item == element { // comparable制約により比較が可能
			return true
		}
	}
	return false
}

func main() {
	stringList := NewMyArrayList[string]()
	stringList.Add("apple")
	stringList.Add("banana")
	stringList.Add("cherry")
	fmt.Println("Original List:", stringList)

	// Containsメソッドのテスト
	fmt.Println("Contains 'banana'?", stringList.Contains("banana")) // true
	fmt.Println("Contains 'grape'?", stringList.Contains("grape"))   // false
	fmt.Println("Contains 'apple'?", stringList.Contains("apple"))   // true

	stringList.Remove(0)
	fmt.Println("List after removal:", stringList)
	fmt.Println("Contains 'apple'?", stringList.Contains("apple")) // false

	intList := NewMyArrayList[int]()
	intList.Add(10)
	intList.Add(20)
	intList.Add(30)
	fmt.Println("Int List:", intList)
	fmt.Println("Contains 20?", intList.Contains(20)) // true
	fmt.Println("Contains 50?", intList.Contains(50)) // false

	// 構造体など、comparableではない型をMyArrayListで使おうとするとコンパイルエラーになる
	// type MyStruct struct {
	// 	Name string
	// 	Value int
	// }
	// structList := NewMyArrayList[MyStruct]() // コンパイルエラー: MyStruct does not satisfy comparable
}

変更点と解説

  • MyArrayList[T comparable]:
    • MyArrayListの型パラメータTcomparable制約を追加しました。
    • これにより、MyArrayListを使用する際に、その要素の型が==や!=演算子で比較可能であることがコンパイル時に保証されます。
    • プリミティブ型(int, string, boolなど)、ポインタ、チャネル、インターフェース、構造体(すべてのフィールドがcomparableである場合)、配列(要素がcomparableである場合)などがcomparableです。
    • スライス、マップ、関数はcomparableではありません。
  • Contains(element T) bool メソッドの追加:
    • このメソッドは、引数elementがリストlist.elements内に存在するかどうかをチェックします。
    • for _, item := range list.elements でリストの各要素をイテレートします。
    • if item == element の部分で、comparable制約のおかげで==演算子を使って要素同士を比較できます。
    • 一致する要素が見つかればtrueを返し、ループが終了しても見つからなければfalseを返します。

comparable制約の重要性
comparable制約がない場合、Containsメソッド内でitem == elementのような比較を行うと、コンパイルエラーになります。Goのジェネリクスは、型パラメータが特定の操作をサポートすることを明示的に宣言する必要があります。comparable制約は、その型が比較可能であることを保証するためのものです。

これで、MyArrayListはJavaのArrayListの主要な機能に加えて、要素の存在チェックも行えるようになりました。

感想

おうふっ
見事にやってほしくないことをやってくれました。
anycomparableに変更しただけ。

MyComparableArrayList 構造体

質問

もうひとつ別の構造体を作ってほしい。
どう聞けばいいのか分からず、コードでそのまま聞いてみました。

type MyArrayList[T any] struct { elements []T }

type MyComparableArrayList[T comparable] struct { MyArrayList[T] }
のふたつにしてください

回答

承知いたしました。MyArrayListMyComparableArrayListの2つの構造体に分け、MyComparableArrayListMyArrayListを埋め込む形で実装します。

これにより、すべての型で利用可能な基本的なMyArrayListと、比較可能な型に限定されるもののContainsメソッドを持つMyComparableArrayListという、役割が明確な2つの構造体を提供できます。

2つの構造体に分割した実装

package main

import "fmt"

// MyArrayList は基本的な動的配列機能を提供します。
// 任意の型の要素を格納できます。
type MyArrayList[T any] struct {
	elements []T
}

// NewMyArrayList は新しいMyArrayListを初期化して返します。
func NewMyArrayList[T any]() *MyArrayList[T] {
	return &MyArrayList[T]{
		elements: make([]T, 0),
	}
}

// MyComparableArrayList は比較可能な要素に特化した動的配列です。
// MyArrayListを埋め込むことで、そのメソッドを継承します。
type MyComparableArrayList[T comparable] struct {
	MyArrayList[T] // MyArrayListを埋め込む
}

// NewMyComparableArrayList は新しいMyComparableArrayListを初期化して返します。
func NewMyComparableArrayList[T comparable]() *MyComparableArrayList[T] {
	return &MyComparableArrayList[T]{
		MyArrayList: *NewMyArrayList[T](), // 埋め込んだMyArrayListを初期化
	}
}

// Contains は指定された要素がリストに含まれている場合にtrueを返します。
// Tがcomparableなので、要素の比較が可能です。
func (list *MyComparableArrayList[T]) Contains(element T) bool {
	for _, item := range list.elements { // MyArrayListのelementsフィールドにアクセス
		if item == element {
			return true
		}
	}
	return false
}

func main() {
	fmt.Println("--- MyArrayList の使用例 ---")
	// 任意の型 (ここでは構造体) を格納できる MyArrayList
	type Person struct {
		Name string
		Age  int
	}
	personList := NewMyArrayList[Person]()
	personList.Add(Person{Name: "Alice", Age: 30})
	personList.Add(Person{Name: "Bob", Age: 25})
	fmt.Println("Person List:", personList)
	fmt.Println("Size:", personList.Size())
	p, err := personList.Get(0)
	if err == nil {
		fmt.Println("First Person:", p.Name)
	}

	// MyArrayListはContainsメソッドを持たない (comparableではないため)
	// personList.Contains(Person{Name: "Alice", Age: 30}) // コンパイルエラー

	fmt.Println("\n--- MyComparableArrayList の使用例 ---")
	// 文字列 (comparable) を格納できる MyComparableArrayList
	stringList := NewMyComparableArrayList[string]()
	stringList.Add("apple")
	stringList.Add("banana")
	stringList.Add("cherry")
	fmt.Println("String List:", stringList)
	fmt.Println("Size:", stringList.Size())

	// Containsメソッドの使用
	fmt.Println("Contains 'banana'?", stringList.Contains("banana")) // true
	fmt.Println("Contains 'grape'?", stringList.Contains("grape"))   // false

	// MyArrayListから継承されたメソッドも使える
	val, err := stringList.Get(1)
	if err == nil {
		fmt.Println("Get(1):", val)
	}
	removed, err := stringList.Remove(0)
	if err == nil {
		fmt.Println("Removed:", removed)
	}
	fmt.Println("String List after removal:", stringList)
	fmt.Println("Size after removal:", stringList.Size())
	fmt.Println("Contains 'apple'?", stringList.Contains("apple")) // false

	// int (comparable) を格納できる MyComparableArrayList
	intList := NewMyComparableArrayList[int]()
	intList.Add(10)
	intList.Add(20)
	fmt.Println("Int List:", intList)
	fmt.Println("Contains 20?", intList.Contains(20)) // true
}

変更点と解説

  • MyArrayList[T any] struct:
    • この構造体は、型パラメータ Tany であるため、任意の型の要素を格納できます。
    • Add, Get, Remove, Size, IsEmpty, Clear, String, Sliceといった基本的な動的配列の機能を提供します。
    • Containsメソッドは持っていません。なぜなら、Tanyである場合、Tの要素が比較可能である保証がないからです。
  • MyComparableArrayList[T comparable] struct:
    • この構造体は、型パラメータTが comparable 制約を持ちます。これにより、T型の要素が==!=で比較できることが保証されます。
    • MyArrayList[T] を埋め込みフィールドとして持ちます。
      • Goの構造体の 埋め込み(embedding) 機能により、MyComparableArrayListMyArrayListが持つすべてのメソッド(Add, Get, Removeなど)を直接呼び出すことができます。これはJavaの継承に似た振る舞いを実現します。
    • MyComparableArrayListのインスタンスに対してlist.Add(...)のように呼び出すと、内部で埋め込まれたMyArrayListAddメソッドが実行されます。
    • NewMyComparableArrayList: このコンストラクタでは、埋め込まれたMyArrayList*NewMyArrayList[T]()で適切に初期化する必要があります。
    • Contains(element T) bool メソッド:
      • Tcomparableであるため、この構造体でのみContainsメソッドを実装できます。
      • 内部のelementsスライス(埋め込みによってアクセス可能)を走査し、item == elementで比較を行います。

この設計のメリット

  • 役割の分離:
    • MyArrayListは汎用的な動的配列の基盤を提供し、comparableでない型(例: スライス、マップ、関数、独自定義だが比較できない構造体)も安全に格納できます。
    • MyComparableArrayListは、要素の比較が必要な場合に特化し、そのためのContainsメソッドを追加します。
  • コードの再利用: MyComparableArrayListMyArrayListの機能を埋め込みによって再利用するため、コードの重複が避けられます。
  • 型安全性: Containsメソッドが必要な場合にのみcomparable制約を課すことで、より厳密な型チェックがコンパイル時に行われます。

この構造により、必要に応じて適切なArrayListのバリアントを選択できるようになりました。

感想

そうそう。
これが欲しかったんです!

まとめ

当然ですがAIとの間で認識が違っていると、なかなか思い通りの回答を出してくれません。
根気よく言い換えていけばいずれ理解してくれるはずです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?