#java #swing #inheritance #scope
#java #swing #наследование #область видимости
Вопрос:
У меня есть три класса, которые показаны ниже, для графического интерфейса игры:-
//this is the parent class.
import javax.swing.*;
import java.awt.*;
public class GameGui extends JFrame{
public void decorateButton(JButton aBut,Color forg,Color back){
Font afont = new Font(Font.SANS_SERIF,Font.PLAIN,18);
aBut.setFont(afont);
aBut.setBackground(back);
aBut.setForeground(forg);
}
public void setFrameDefault(){
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 475);
this.setLocationRelativeTo(null);
this.setResizable(false);
}
public void setConstraints(int x,int y,int weightx,int weighty,GridBagConstraints gbc){
gbc.weighty=weighty;
gbc.weightx=weightx;
gbc.gridx=x;
gbc.gridy=y;
}
}
//this class is for result to be shown for the game.
import javax.swing.*;
import java.awt.*;
class Result extends GameGui{
JPanel mainPanel = new JPanel();
JLabel backImage = new JLabel();//I want this variable to be shadowed by the subclass variable,but it is not happening.
JButton continueGame = new JButton("continueGame");
JButton exitGame = new JButton("exitGame");
public Result(){
this.setFrameDefault();
backImage.setLayout(new BorderLayout());
this.setContentPane(backImage);
mainPanel.setLayout(new GridBagLayout());
decorateButton(continueGame,Color.green,Color.white);
decorateButton(exitGame,Color.green,Color.white);
setGui();
}
public void setGui(){
GridBagConstraints gbc = new GridBagConstraints();
mainPanel.setOpaque(false);
gbc.gridy=200;
gbc.gridx=0;
gbc.insets=new Insets(410,0,0,130);
mainPanel.add(continueGame,gbc);
gbc.gridx=GridBagConstraints.RELATIVE;
gbc.insets = new Insets(410,0,0,0);
mainPanel.add(exitGame,gbc);
setFrameDefault();
this.getContentPane().add(mainPanel);
}
}
//this class is for showing the result for a Win.
import javax.swing.*;
import java.awt.*;
public class Win extends Result{
JLabel backImage = new JLabel(new ImageIcon("C:\Users\BSK\Desktop\win.png"));//Problem is here as i have declared the same named JLabel as in Result class but iam not getting the image as background.
public static void main(String[] args) { //this main method is for testing.
Win w = new Win();
w.setVisible(true);
}
}
Мне нужны два класса в конце иерархии, которые являются Win
и Defeat
(второй я не реализовал).Мне это нужно, потому что я хочу, чтобы фрейм победы и фрейм поражения отличались только изображением.
Итак, мой вопрос заключается в том, что, хотя я объявил JLabel с тем же именем, что и backImage
в обоих классах Result
и Win
, почему я не получаю изображение в фоновом режиме?Я протестировал это, поместив изображение в класс JLabel
backImage
of Result
, и тогда это работает!Но я хочу воспользоваться преимуществом затенения данных, потому что в моем Defeat
классе (который также расширяет Result) я буду использовать имя, JLabel
имеющее то же имя, что и backImage
, но с другим изображением, установленным для него.Я надеюсь, вы понимаете, так каков же выход?
Заранее спасибо.
ПРИМЕЧАНИЕ пожалуйста, протестируйте с вашим изображением.
Комментарии:
1. Похоже, что вы злоупотребляете наследованием здесь: вы используете наследование там, где композиция была бы лучше.
2. @HovercraftFullOfEels если вы можете предоставить подробный ответ ……много-много-много раз спасибо
3. @HovercraftFullOfEels, пожалуйста, если вы можете описать…. жду вашего ответа.
Ответ №1:
Затенение влияет на то, к какой переменной относится имя. То есть, поскольку подкласс Win
определяет свою собственную backImage
переменную экземпляра, методы Win
, которые ссылаются на backImage
, будут ссылаться на переменную экземпляра в Win
(и, следовательно, на ее значение), а не на переменную в суперклассе, Result
.
Затенение не заменяет объект, на который указывают переменные и другие объекты. То есть суперкласс Result
по-прежнему определяет свою собственную backImage
переменную экземпляра, а Result
методы по-прежнему ссылаются на эту переменную (и, следовательно, на ее значение). Итак, Win#backImage
тени Result#backImage
, но это не меняет того, как работает результат.
Также обратите внимание, что строки инициализации, подобные JLabel backImage = ...
, выполняются как часть конструктора класса, а конструктор подкласса Win
начинается с запуска его конструктора суперкласса Result
. Итак, если подкласс не объявлял другой, backImage
а его конструктор присвоил новое значение унаследованной переменной экземпляра Result#backImage
, это произошло бы после того, как Result
конструктор создал панель содержимого, поэтому отображение не изменилось бы.
Вы могли бы изменить содержимое backImage
объекта:
public class Win extends Result {
public Win() {
super();
backImage.setIcon(new ImageIcon("C:\Users\BSK\Desktop\win.png"));
}
...
чтобы изменить значок обратного изображения для Win
подкласса.
Комментарии:
1. пожалуйста, уточните свою первую строку .. если можете… Спасибо
2. Доработано. Коду также может потребоваться запросить ретрансляцию окна после изменения значка метки.
3. вы говорите, что
Win
класс будет ссылаться на свой собственный JLabel, но тогда почему результат не соответствует ожиданиям?4. Нет. Класс Win не должен объявлять свою собственную переменную backImage или создавать другой экземпляр JLabel. Просто измените значок существующего JLabel, который уже связан с деревом компонентов.
5. Кстати, сообщение выше отвечает на вопрос о том, что делает затенение, а что нет, и предлагает минимальное изменение кода. В лучшем дизайне можно было бы исключить подкласс (это не очень хорошее использование подклассов) и либо добавить
Result
метод для изменения его фона, либо добавитьResult
параметр конструктора для указания фона. В последнем случае может необязательно использоваться конструктор без аргументов, который всегда использует фон по умолчанию.
Ответ №2:
Поля в Java не переопределяются как методы, поэтому у вас это не сработает. К счастью, существует множество решений.
Один из них (я определенно не говорю, что лучший!) заключается в том, чтобы сделать вашу Result
игру абстрактной с помощью абстрактного getIcon()
метода или чего-то еще и вызвать это из вашего setGui()
метода, тогда ваши классы Win / Lose будут просто возвращать разные значки там.
Комментарии:
1. но это не переопределение, и как
Result
класс будет использовать другие свои методы, если мы неJLabel
определили их там?2. JLabel все равно будет удален внутри
Result
класса, и методgetIcon()
также будет объявлен там, просто инициализируйте свой background вsetGui()
, но вместо определенного значка используйте свойgetIcon()
метод. Затем переопределите это в вашемWin
классе, чтобы вернуть значок, который вы действительно хотите.3. Хотя
Win
можно переопределитьResult#getIcon()
метод, обычно это плохая идея переопределять метод, вызываемый конструктором, потому что он вызывается до запуска конструктора подкласса. Это можно исправить, выполнив работу по созданию пользовательского интерфейса после конструктора.4. aaa, верно, я хотел добавить предупреждение о том, что это ужасная идея вызывать переопределяемые методы из конструктора (из собственного опыта), я не заметил, что
setGui()
был вызван из конструктора.