A.R.H
A.R.H

Reputation: 391

Java Generics : How to avoid casting in this example of java 2D generic arrays?

I'm modeling a board, I wanted to make it as generic as I can,I also wanna avoid casting at all cost, because it's a bad practice!

Also, My code is completely working as right now, but I know as soon as I start working with Cell class and extend it, it'll be problematic!

Cell.java

public class Cell<T> {
    private t value;

    public Cell(T value) {
        this.value = value;
    }

    // setters and getters
}

Board.java

import java.lang.reflect.Array;

public abstract class Board<T, E extends Cell<T>> {
    protected E[][] cells;

    protected Board(Class<? extends E> c) {
        cells = (E[][])Array.newInstance(c, 6, 7);
    }

    protected void resetBoard(T resteVal) {
        for (int i = 0; i < cells.length; i++)
            for (int j = 0; j < cells[i].length; j++)
                cells[i][j]= (E) new Cell<T>(); //HERE IS THE QUESTION
    }
}

Game.java

public interface Game {
    boolean islegalMove();
    boolean isWin();
    void move();
    void resetGame();
    void getScore();
}

Connect4.java

public class Connect4<E extends Cell<Integer>> extends Board<Integer,E> implements Game {
    public Connect4(Class<? extends E> c) {
        super(c);
    }

    // override methods
}

Upvotes: 0

Views: 69

Answers (2)

Ele
Ele

Reputation: 33726

Replace this protected E[][] cells; by protected Cell<T>[][] cells;

public abstract class Board<T, E extends Cell<T>> {
    protected Cell<T>[][] cells;
    @SuppressWarnings("unchecked")
    public Board(Class<? extends E> c) {
        cells = (E[][])Array.newInstance(c, 6, 7);
    }

    protected void resetBoard(T resteVal) {
        for (int i = 0; i < cells.length; i++) {
            for (int j = 0; j < cells[i].length; j++) {
                Cell<T> cell =  new Cell<T>();
                cell.setValue(resteVal);
                cells[i][j] = cell; //HERE IS MY REPLACEMENT
            }
        }
    }
}

Upvotes: 1

Oleg Cherednik
Oleg Cherednik

Reputation: 18255

I think the problem is in architecture.

You have define Cell<T> class (e.g. it could be extendable).

Then you define an abstract Board, that could work with custom Cell.

public abstract class Board<T, E extends Cell<T>> {
    // why do not use T[][] cells instead?
    protected final E[][] cells;

    protected Board(E[][] cells) {
        this.cells = cells;
    }

    // we hole Cell instances inside this class, so no need to create new ones
    protected void resetBoard(T resetVal) {
        for (int i = 0; i < cells.length; i++)
            for (int j = 0; j < cells[i].length; j++)
                cells[i][j].setValue(resetVal);
    }
}

And finally, we could defeine an concrete board with concrete cell instance.

public final class MagicBoard<T> extends Board<T, MagicBoard.MagicCell<T>> {

    public MagicBoard(Supplier<MagicCell<T>> supplier) {
        //noinspection rawtypes,unchecked
        super(new MagicCell[6][7]);
    }

    public static final class MagicCell<T> extends Cell<T> { }
}

Resume:

  1. Abstract class Board should not ware about concrete cell instance creating. Maximum, it could accept Supplier from child class to delegate this work to concrete class 'MagicBoard' (only this class knows what type of Cell should be created).

  2. Try to avoid useless Cell instance creating. If you hold it inside Board and do not share it outside it, then reuse it. Just reset the value.

  3. Question! I do not know whole code, but if your Cell contains only one value, you could avoid using Cell and use protected final T[][] cells; instead (I think, that Board should contains all logic for working with Cell, but cells itself should not be very clever - just keep simple value). But this is only my opinion.

Upvotes: 1

Related Questions