Mam problem z używaniem argumentów typu w klasach języka Java. Mam jedną superklasę o nazwie Game

public abstract class Game<T extends Player> implements Serializable {
    public Game(GameEnum game, Player player1, T player2) {
        this.timer = new Timer();
        this.game = game;
        this.player1 = player1;
        this.player1.setPlayer1();
        this.player2 = player2;
   }
}

Jest też moja klasa, która ją rozszerza

public abstract class OtherGame<T extends Player> extends Game<T> {
    public OtherGame(GameEnum game, Player player1, T player2) {
        super(game, player1, player2);
    }
}

Jest też Class ChessGame, która rozszerza Class OtherGame

public class ChessGame<T extends Player> extends OtherGame<T> {
    public ChessGame(Player player) {
        super(GameEnum.CHESS, player, new ChessComputer());

        //Here is the Error that the Constructor with the Param. ChessComputer
        //does not exist and i can cast the ChessComputer to T.

        ((Computer) super.player2).setInput(this.chessboard);
        this.playerFile = new File(CHESSGAME, String.format("%s vs. Computer%s", super.player1, FILEEXT));
        this.comFile = new File(CHESSGAME, super.player1.hashCode() + "");
        this.gameMenu();
    }
}

Teraz mam też kilka klas PlayerClass

public class Player implements Serializable {
    public Player(String name, boolean player1) {
        this.name = name;
        this.player1 = player1;
    }
}

I moja Computerclass, która rozszerza Playerclass

public abstract class Computer extends Player {
    public Computer() {
        super("Computer", false);
    }
}

I mam ChessComputerclass, która rozszerza klasę Computerclass

public class ChessComputer extends Computer {
    public ChessComputer() {
        super();
    }
}

Mam wtedy klasę, która wywołuje konstruktora ChessGameclass w następujący sposób:

new ChessGame<ChessComputer>(this.player);
//Here is no error

Dlaczego jest błąd, że nie ma takiego Konstruktora, ponieważ pomyślałem, że jeśli użyję zmiennej typu, takiej jak T, mogę dać Konstruktorowi dowolną podklasę tej, o której powiedziałem, że będzie rozszerzać. Wzywam również ChessGame Constructor za pomocą Typeargument ChessComputer, o którym myślałem, że „da” T klasę ChessComputer. Wiem trochę o Typearguments io tym, jak działają w Javie, ale oczywiście nie wystarczy.

Przepraszam, jeśli nie używam właściwych słów, aby to opisać, to dlatego, że angielski nie jest moim pierwszym językiem.

1
MineRickStar 19 listopad 2019, 23:41
1
Jak definiuje się zmienną this.player?
 – 
marstran
19 listopad 2019, 23:52
1
Jeśli chcesz, aby ChessGame<ChessComputer> brał tylko ChessComputer jako argument konstruktora, musisz zdefiniować parametr konstruktora w ChessGame za pomocą typu T: public ChessGame(T player) .
 – 
marstran
19 listopad 2019, 23:56
W moim programie startowym użytkownik jest proszony o podanie nazwy, abym mógł stworzyć normalnego gracza, a następnie może zdecydować się grać sam przeciwko komputerowi lub przeciwko innemu graczowi
 – 
MineRickStar
19 listopad 2019, 23:56
Prawdopodobnie udostępnij swój kod na github lub innym publicznym repozytorium, aby ludzie mogli go sklonować i zobaczyć cały kod i błędy.
 – 
Sergei Sirik
19 listopad 2019, 23:57
Chcę, aby ChessGame wzięło albo komputer szachowy, albo gracza.
 – 
MineRickStar
19 listopad 2019, 23:58

3 odpowiedzi

Tutaj struktura pary klas pochodnych klasy ma ogólne ograniczenie typu:

public class ChessGame<T extends Player> extends OtherGame<T>
    public ChessGame(Player player) {
        super(GameEnum.CHESS, player, new ChessComputer());

Trzeci parametr konstruktora OtherGame jest ograniczony do typu T, nawet jeśli jest to podtyp ChessComputer. Wymaga rzutu T, takiego jak super(GameEnum.CHESS, player, (T) new ChessComputer());

Z drugiej strony, jak skomentował @jrook, użycie typu ogólnego nie jest tutaj odpowiednie, jeśli nie masz innych planów. Powinieneś rozważyć użycie interfejsów jako klasy Player implementującej wspólny interfejs IPlayer i możesz przekazać podklasy do konstruktorów innych klas.

1
ashemez 20 listopad 2019, 00:32
Próbowałem już tego, ale wtedy mówi niezaznaczone Cast from ChessComputer do T. I tego chcę uniknąć.
 – 
MineRickStar
20 listopad 2019, 00:33
Tak, to jest problem i sugerowałem powyżej rozważ użycie interfejsów. Zdefiniuj interfejs gracza. public interface IPlayer Następnie zaimplementuj go w klasie Player i usuń wszystkie typy ogólne..
 – 
ashemez
20 listopad 2019, 00:36
Interfejs był dobrym pomysłem, ale muszę zaimplementować te same funkcje w odtwarzaczu i klasie komputerowej, co nie jest tym, czego chcę, ponieważ mają one różne funkcje razem.
 – 
MineRickStar
20 listopad 2019, 11:05

Rozszerzanie klas ogólnych o inne typy generyczne jest problematyczne. Nie chodzi o to, że twój kod jest zły, ale kompilator może zrobić tylko tyle, aby go ocenić, a użycie generyków w Javie nie jest idealne.

Najlepszym rozwiązaniem, jakie znalazłem, jest próba ograniczenia poziomu odniesienia do typów ogólnych do bieżącej klasy lub może jednego super. Poza tym są problemy.

To rozwiązuje problem:

public class ChessGame<T extends Player> extends OtherGame<Player>
1
Jason Warner 20 listopad 2019, 01:22

Myślę, że mam „dobre” rozwiązanie,

Zmieniłem Konstruktora mojej klasy ChessGame

public class ChessGame<T extends Player> extends OtherGame<T> {
    public ChessGame(Player player, T player2) {
        super(GameEnum.CHESS, player, player2);
        if (super.player2 instanceof Computer) {
            ((Computer) super.player2).setInput(this.chessboard);
        }
        this.playerFile = new File(CHESSGAME, String.format("%s vs. Computer%s", super.player1, FILEEXT));
        this.comFile = new File(CHESSGAME, super.player1.hashCode() + "");
        this.gameMenu();
    }
}

I zadzwoń do Konstruktora jak

new ChessGame<Computer>(this.player, new ChessComputer());

Nadal muszę rzucić gracza, ale ponieważ jest to rzut obronny, myślę, że nie ma się czym martwić.

0
MineRickStar 20 listopad 2019, 12:30
Tak, widzisz, faktycznie użyłeś w niewłaściwy sposób new ChessComputer() jako parametru super(). Bez znajomości zachowania Twojego kodu nie jest łatwo zrozumieć, co chcesz osiągnąć. Interfejsy są w rzeczywistości najlepszą praktyką wzorca abstrakcji projektu OOP, niezależnie od generowania. Dobrym pomysłem byłoby również zapoznanie się z wzorcami projektowania oprogramowania link
 – 
ashemez
20 listopad 2019, 13:32
To nie jest to, czego chcę, nie chcę mieć konstruktora, który przyjmuje dwóch graczy, gdy jestem w trybie dla jednego gracza, chcę, aby jeden gracz i konstruktor dał superkonstruktorowi komputer. Jeśli używam interfejsu IPlayer, odtwarzacz i komputer muszą zaimplementować te same metody, ale tak naprawdę nie dzielą żadnych metod.
 – 
MineRickStar
20 listopad 2019, 15:18