I am trying to use the Methods from a class that i have in an Array list.
The ArrayList is ArrayList With Appliance being a super Class.
the ArrayList contans objects that extends Appliance such as a Clock and Lamp.
when i use
arrayList.get(x) to grab the object at that point i cant see the methods that the object has, I can only see the superClass Objects.
Please can someone help me.
Thank you for reading
Code (Some of it)
public abstract class Appliance implements Serializable {
protected boolean power;
protected ImageIcon picture;
public Appliance() {
}
public void setPower(boolean power) {
this.power = power;
}
public boolean getPower() {
return power;
}
abstract ImageIcon getPicture();
#Override
public String toString() {
String powerVal;
if (this.power == true) {
powerVal = "ON";
} else {
powerVal = "OFF";
}
return "Power: " + powerVal;
}
}
public class Clock extends Appliance {
private int hours;
private int minutes;
private int seconds;
public Clock() {
super();
this.power = false;
this.picture = new ImageIcon("src/res/clock.jpg");
this.hours = 23;
this.minutes = 59;
this.seconds = 59;
}
public Clock(boolean pow, int hours, int minutes, int seconds) {
super();
this.power = pow;
this.picture = new ImageIcon("src/res/clock.jpg");
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;
}
public int getHour() {
return this.hours;
}
public void setHours(int hours) {
this.hours = hours;
}
public int getMinutes() {
return this.minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
public int getSeconds() {
return this.seconds;
}
public void setSeconds(int seconds) {
this.seconds = seconds;
}
#Override
public ImageIcon getPicture() {
return this.picture;
}
#Override
public String toString(){
return super.toString() + String.format(" and the time is %d:%d:%d",this.hours, this.minutes, this.seconds);
}
}
public class Lamp extends Appliance{
//Default constructor or Empty argument constructor
public Lamp(){
super();
this.power = false;
this.picture = new ImageIcon("src/res/lamp.jpg");
}
public Lamp(boolean pow){
super();
this.power = pow;
this.picture = new ImageIcon("src/res/lamp.jpg");
}
#Override
ImageIcon getPicture() {
return picture;
}
#Override
public String toString(){
return super.toString();
}
}
public class Controller {
private ArrayList<Appliance> myAppliances = new ArrayList<>();
private JLabel[] labelArray;
...................................................
#Override
public void mouseClicked(MouseEvent me) {
String[] options = new String[]{"yes","no"};
if (me.getButton() == 1){
try{
int x = Integer.parseInt( me.getComponent().getName());
Appliance myApp = this.myAppliances.get(x);
if(myApp.getClass().equals(Clock.class)){
JOptionPane.showOptionDialog(null, "Clock Info: /nTime: " + myApp., "Clock", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,options,options[1] );
}
} catch (Exception e){
System.out.println("Null Poiter");
}
}
}
}
Its the myApp. part in the clicked method
I assume you are doing something like this (sorry but it is not clear from your question):
ArrayList<SuperClass> al;
...populated with instances of Appliance
al.get(1).getClock(); //Compile error.
The problem is that java does not know if your element at that position is a SuperClass, Appliance, or something else that inherits from SuperClass. You can cast the code, to make it behave the way you want:
((Appliance)al.get(1)).getClock();
You may also want to use the instanceOf operator to make sure you do have an instance of the class you are expecting.
It sounds like you're writing
List<Appliance> appliances = new ArrayList<>();
appliances.add(new Lamp());
appliances.add(new Clock());
Appliance appliance = appliances.get(0);
appliance.setAlarm(TOMORROW);
I think from this example you can see why you can't access the subclass methods. When you have a list of Appliance, you don't know if the objects in it are Clocks or Lamps, so when you get one out you can't call the subclass methods.
If you are positive the object is a clock, you could cast it:
Clock clock = (Clock) appliances.get(1);
clock.setAlarm(TOMORROW);
But this is not the Java Way. Typically you would use only superclass methods on Appliance, or maintain a separate list for Clocks and Lamps.
ArrayList<Appliance> mylist = new ArrayList();
for(Appliance app : mylist){
boolean isAclock = app instanceof Clock;
boolean isALamp = app instanceof Lame;
if(isAlamp){
Lamp l = (Lamp)app;
}else{
Clock c = (Clock)app;
}
}
making your arralist to accept appliacnces will accept its sub classes bt the problem is receving the correct class object . you can use instance of and check the object type and then cast the object to its original type
try using this approach
That's the expected behavior. It's how polymorphism works.
Let's take a look at an example using your own classes. Imagine holding a bag. You know that only Appliance objects can go into this bag, so you go around all the appliances in your house into this bag - your Lamp, your Clock, maybe your Toaster and a Blender, and a bunch of others.
Now imagine you put on a blindfold and randomly pull out one of the appliances. Without looking at it, how can you tell what appliance it is? You can't! If you assume the thing you just pulled out was a Toaster and you tried to toast() your bread with it, what would happen if you were actually holding a Blender? You'd get a bloody mess, probably. Therefore, the only thing you know for a fact that all Appliance objects can do is turnOn() or turnOff(), so those are the only methods available to you.
In this example, you are the compiler and the bag is the list. If you tell the compiler that the list can hold only Appliance objects, it will not assume anything else about the objects in the list unless you explicitly tell it to (for example, if you downcast the Appliance object to a Toaster by doing something like ((Toaster)applianceList.get(0)).toast()).
Related
So I have this assignment which I need to put data into a tree set. I have three classes they are :
Brother.java. The assignment said the constructor is not public, so I'm using the getInstance() to initialize Brother object
public class Brother {
String name;
int day;
int month;
private static Brother instance = null;
private Brother()
{
name = "0";
day = 0;
month = 0;
}
public static Brother getInstance()
{
if(instance == null)
{
instance = new Brother();
}
return instance;
}
}
Family class. This class is used to assign the brother object into the tree set with Brother as the objects.
import java.util.Set;
import java.util.TreeSet;
public class Family {
Set<Brother> Brothers;
public Family()
{
this.Brothers = new TreeSet<Brother>();
}
public Brother makeBrother()
{
Brother B = Brother.getInstance();
return B;
}
public boolean addBrother(String name, int day, int month)
{
Brother B = Brother.getInstance();
return Brothers.add(B);
}
}
And finally the main class
public class Main {
public static void main(String[] args) {
Family myFamily = new Family();
myFamily.makeBrother();
// myFamily.addBrother("Shane", 3, 2);
}
}
whenever I try to use myFamily.addBrother() I always got this error "Exception in thread "main" java.lang.ClassCastException: class Brother cannot be cast to class java.lang.Comparable (Brother is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')". What do I have to do with that? The program is perfectly fine when I use myFamily.makeBrother(). This algorithm is not all done yet but when I try to run it this happened to me and I cannot continue to the next step. Thank you before.
You are using a Set instead of a List because you want to avoid duplicates. To know which Brothers are duplicate, a TreeSet needs either a comparator, or the objects themselves need to implement Comparable.
Read the javadoc of TreeSet for more: https://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html
BTW that getInstance always returns the same instance. You'll probably need to change that to createInstance or something that actually creates new ones.
I agree with #GreyFairer, you have to provide a comparator in order to use Set, see this example:
Class Cast Exception problems with TreeSet
I hope it can helps you!
I see a few problems there.
You have defined Brother as a singleton. That means only one Brother instance will exist in your program. So all references to Brother will point to the same instace. I would avoid singleton for this class since doesn't makes sense.
If you what to use a TreeSet (without providing a Comparator for the Tree) then Brother must implement Comparable
Family.makeBrother just return the singleton Brother, but does not add it to the family tree, that's why you don't get the error.
This is a working rework of your code
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
public class FamilyTreeSet {
public static void main(String[] args) {
Family myFamily = new Family();
myFamily.addBrother("Shane", 3, 2);
myFamily.addBrother("Bob", 2, 4);
System.out.println(myFamily);
}
public static class Brother implements Comparable<Brother>{
private String name;
private int day;
private int month;
public Brother(String name, int day, int month) {
this.name = name;
this.day = day;
this.month = month;
}
public String getName() {
return name;
}
public int getDay() {
return day;
}
public int getMonth() {
return month;
}
#Override
public int compareTo(Brother o) {
return Comparator.comparing(Brother::getName, String.CASE_INSENSITIVE_ORDER).compare(this, o);
}
#Override
public String toString() {
return "Brother{" +
"name='" + name + '\'' +
", day=" + day +
", month=" + month +
'}';
}
}
public static class Family {
Set<Brother> Brothers;
public Family()
{
this.Brothers = new TreeSet<Brother>();
}
public boolean addBrother(String name, int day, int month)
{
Brother B = new Brother(name, day, month);
return Brothers.add(B);
}
#Override
public String toString() {
return "Family{" +
"Brothers=" + Brothers +
'}';
}
}
}
I'm using the each brother's name to compare brothers. Check that I first add Shane but in the output Bob goes first. If you want to compare brothers by birthday just change the compareTo.
Family{Brothers=[Brother{name='Bod', day=2, month=4}, Brother{name='Shane', day=3, month=2}]}
My current problem is that I am assigned to created a program that should within the private fields assign tasks[] an array of task. Then within the constructor, that creates the task[] array, giving it the capacity of INITIAL_CAPAITY, and setting numTasks to zero.
I am new and confused on I can tackle this problem
I have tried declaring it within the constructor but there has been no luck.
Task.java
public class Task {
private String name;
private int priority;
private int estMinsToComplete;
public Task(String name, int priority, int estMinsToComplete) {
this.name=name;
this.priority=priority;
this.estMinsToComplete = estMinsToComplete;
}
public String getName() {
return name;
}
public int getPriority() {
return priority;
}
public int getEstMinsToComplete() {
return estMinsToComplete;
}
public void setName(String name) {
this.name = name;
}
public void setEstMinsToComplete(int newestMinsToComplete) {
this.estMinsToComplete = newestMinsToComplete;
}
public String toString() {
return name+","+priority+","+estMinsToComplete;
}
public void increasePriority(int amount) {
if(amount>0) {
this.priority+=amount;
}
}
public void decreasePriority(int amount) {
if (amount>priority) {
this.priority=0;
}
else {
this.priority-=amount;
}
}
}
HoneyDoList.java
public class HoneyDoList extends Task{
private String[] tasks;
//this issue to my knowledge is the line of code above this
private int numTasks;
private int INITIAL_CAPACITY = 5;
public HoneyDoList(String tasks, int numTasks, int INITIAL_CAPACITY,int estMinsToComplete, String name,int priority) {
super(name,priority,estMinsToComplete);
numTasks = 0;
tasks = new String[]{name,priority,estMinsToComplete};
//as well as here^^^^^^^^
}
My expected result is to be able to print out the list through honeydo class. I need to manipulate the code a bit more after adding a few other methods.
Your problem is that your constructor parameter tasks has the same name as that field of your class.
So you assign to the method parameter in your constructor, not to the field. And luckily those two different "tasks" entities have different types, otherwise you would not even notice that something is wrong.
Solution: use
this.tasks = new String...
within the body of the constructor!
And the real answer: you have to pay a lot attention to such subtle details. And by using different names for different things you avoid a whole class of issues!
Also note: it sounds a bit strange that a class named Task contains a list of tasks, which are then strings. The overall design is a bit weird...
I need to update a fields value inside one of my object, just update the String. The object is inside of an array. But I will target the object by typing in the objects "regNum" in the parameter.
This is what I tried, I really don't know how to use the set() method when I need to enter the list and the objects specific value.
public boolean doesNotWork( String regNumInput ){
for(int i = 0; i < meterList.size(); i++){
if(regNumInput == meterList.get(i).getRegNum()){
meterList.set(meterList.get(i).getWorkOrNot(), new String ("No"));
}
}
return true;
}
This Is the whole MeterArchive class that stores the meters and have some methods to it.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MeterArchive
{
// instance variables - replace the example below with your own
ArrayList<Meter> meterList = new ArrayList<Meter>();
public void createClocks(){
Clock clockOne = new Clock("KH001", "Yes", "ClassRoom005", 0.0);
meterList.add(clockOne);
Clock clockTwo = new Clock("KH002", "Yes", "ClassRoom006", 0.0);
meterList.add(clockTwo);
}
public boolean doesNotWork( String regNumInput ){
for(int i = 0; i < meterList.size(); i++){
if(regNumInput == meterList.get(i).getRegNum()){
meterList.set(meterList.get(i).getWorkOrNot(), new String ("No"));
}
}
return true;
}
public void showAllMeter(){
for(Meter meter : meterList){
System.out.println(meter);
}
}
}
This is the clock class that has specific clock values that you can add.
public class Clock extends Meter
{
/**
* Constructor for objects of class Clock
*/
public Clock(String regNum, String workOrNot, String location, double minTime)
{
// initialise instance variables
super(regNum, workOrNot, location);
setMinTime(minTime);
}
//MINNIMUM TIME
public void setMinTime(double minTime){
this.minTime = minTime;
}
public double getMinTime(){
return minTime;
}
//EQUALS METHOD --- NOT SURE WHAT IT SHOULD DO... YET!
public boolean equals (Clock other){
return location.equals(other.location);
}
public String toString(){
String retur = super.toString() + "regNum: " + regNum +
"Does it work: " + workOrNot +
"Location: " + location +
"Min time value: " + minTime;
return retur;
}
}
This is the super class that has more general input for the different meters.
public class Meter
{
public String regNum;
public String workOrNot;
public String location;
/**
* Constructor for objects of class Clock
*/
public Meter(String regNum, String workOrNot, String location)
{
// initialise instance variables
setRegNum(regNum);
setWorkOrNot(workOrNot);
setLocation(location);
}
//REGISTRATION NUMBER
public void setRegNum(String regNum){
this.regNum = regNum;
}
public String getRegNum(){
return regNum;
}
//WORK OR NOT
public void setWorkOrNot(String workOrNot){
this.workOrNot = workOrNot;
}
public String getWorkOrNot(){
return workOrNot;
}
//LOCATION
public void setLocation(String location){
this.location = location;
}
public String getLocation(){
return location;
}
}
So in the MeterArchive class I want to change the field value "workOrNot" from whatever it is (most likely "Yes") to "No". I found out that set() is usually the way to go, but in this program I want the user to add the specific "regNum" and then the method will change to String inside the "workOrNot" field to "No". As I said earlier I dont know how to target the specific field inside the object. Can someone explain how to do this?
You need to use setter method setWorkOrNot() to update field workOrNot on the desired Meter object.
Use the below code:
public boolean doesNotWork( String regNumInput ){
for(int i = 0; i < meterList.size(); i++){
if(regNumInput.equals(meterList.get(i).getRegNum())){
meterList.get(i).setWorkOrNot("No");
}
}
return true;
}
I'm writing a game, as part of this players should be able to click on various GUI items and see further details on a specific area of the GUI. I'm mangaing this through an interface Detailable which is implemented by suitable game obects and sends the appropriate information to the JPanel
There are also containers (all of which implement Detailable) that contain other (Detailable implementing) objects. The goal being it is possible to click on a container and, amongst its stats, see its contents which can then be in turn clicked on to see their stats, etc.
The problem I am having is in writing the addToContents(Detailable d) method of my containers. Each container as an ArrayList<String> of the "type" of container - wardrobe, bookcase, etc. I want to be able to add only certain classes to a given container - so a container with a type of "bookcase" will only accept objects of class Book or Curio for example.
What I currently have is:
public boolean addToContents(Detailable d){
if(this.types.contains("bookcase") && d.getClass().getName().equals("Book")){
//do some stuff
//I know "Book" isn't the right syntax, this is just to demo
return true;
}
else if(this.types.contains("bookcase") && d.getClass().getName().equals("Curio")){
//other stuff
return true;
}
//etc
else{
return false;
}
}
But this feels like the wrong way of doing it. Is there a better way? Ideally, for sake of easy code, I'd have something like (pseudocode)
Constructor:
private ArrayList<Class> classesAccepted = <list of classes>
addToContents:
if (classesAccepted.contains(d.getClass()){
add the thingie to contents
return true
}
else{
return false;
}
but I can't seem to find a way of adding a list of classes to the constructor - of translating the ArrayList of class names to an ArrayList of references to the actual class.
Containers are currently read from a JSON so comprise two classes:
public class FurnitureType {
private String name;
private List<String> type;
private int cost;
private String description;
private int comfortBonus;
private int capacity;
//plus getters for all the above
}
public class Furniture implements Detailable, ListSelectionListener{
private String name;
private List<String> types;
private int cost;
private String description;
private int comfortBonus;
private int capacity;
private ArrayList<Detailable> contents;
private transient DetailPanel dp = null;
public Furniture (FurnitureType type){
this.name=type.getName();
this.types = type.getType();
this.cost = type.getCost();
this.description = type.getDescription();
this.comfortBonus = type.getComfortBonus();
this.capacity = type.getCapacity();
this.contents = new ArrayList();
}
//appropriate getters
public boolean addToContents(Detailable d){
if(this.types.contains("bookcase") && d.getClass().getName().equals("Book")){
//do some stuff
//I know "Book" isn't the right syntax, this is just to demo
return true;
}
else if(this.types.contains("bookcase") && d.getClass().getName().equals("Curio")){
//other stuff
return true;
}
//etc
else{
return false;
}
}
#Override
public String toString(){
return description;
}
#Override
public Icon getBigPic() {
return null;
}
#Override
public JComponent getStats() {
Object [] objectContents = contents.toArray();
JList contentList = new JList(objectContents);
contentList.setPreferredSize(new Dimension (400, 300));
contentList.setFixedCellHeight(50);
contentList.addListSelectionListener(this);
contentList.setCellRenderer(new CustomCellRenderer());
//the CustomCellRenderer class simply makes long descriptions into multiline cells
return contentList;
}
#Override
public void addPanel(DetailPanel dp) {
this.dp = dp;
}
#Override
public void valueChanged(ListSelectionEvent lse) {
Detailable d = contents.get(lse.getFirstIndex());
dp.updatePanel(d);
}
You can actually use a Map as shown below:
private static Map<String, List<Class<? extends Detailable>>>
bookcaseContainer = new HashMap<>();
static {
//load the bookcaseContainer Map from properties/database
bookcaseContainer.put("bookcase", list1);
bookcaseContainer.put("wardrobe", list2);
}
if(bookcaseContainer.get("bookcase") != null &&
bookcaseContainer.get("bookcase").contains(d.getClass())) {
//do something here
} else if(bookcaseContainer.get("wardrobe") != null &&
bookcaseContainer.get("wardrobe").contains(d.getClass())) {
//do something here
}
If I understand your question correctly, you are looking for something like this
ArrayList <Class<? extends Detailable>> acceptedClasses = new ArrayList<>();
acceptedClasses.add(Bookcase.class);
acceptedClasses.add(OtherAcceptable.class);
and then do something akin to
boolean test =
acceptedClasses.stream().anyMatch(clazz -> aClass.isInstance(detailableInstance));
to check if the instance is of an acceptable type
I have the following:
Class 1:
public class SellProduct
{
private int productCost;
public SellProduct(int productCost)
{
this.productCost = productCost;
}
public int getProductCost()
{
return productCost;
}
}
This class will set how much a product costs.
Class 2:
public class SalesOfTheYear
{
private int totalIncome;
SellProduct sellProduct;
public SalesOfTheYear()
{
totalIncome = 0;
}
public void cashOut()
{
totalIncome = sellProduct.getProductCost() + totalIncome;
}
public int getSalesOfTheYear()
{
return totalIncome;
}
}
Now what I want is that class two to take how much the products cost and then set it to the totalIncome field. And of course to keep it's value at the same time and not replace it with a new totalIncome value.
However, every time I run cashout it sends a java.lang.NullPointerException. Does this mean I have to create an object of class sellPoduct?
And if do I would have to supply it with a parameter does that mean that whatever I supply it with a parameter so will it always be the productCost?
Yes, it makes sense to pass product cost to cashOut() method and add it into totalIncome rather than storing reference of SellProduct itself. It will look like this:
public void cashOut(int cost){
totalIncome += cost;
}
Also, we don't need default constructor in SalesOfTheYear class as int literals are assigned default values (0 in this case) when object is created.
Yes in Java whenever you have your own classes like SellProduct you will have to initialise it with:
SellProduct sellProduct = new SellProduct(xxx);
with xxx being an integer in your case
If you give it the number 20 then your totalIncome will increase with 20 every time you run cashOut()
To update the totalIncome field in a SalesOfTheYear instance, you need to transmit all required SellProduct instances to SalesOfTheYear .
Either, you have all the instances and you provide them in one time, either you may provide instance by instance.
public class SalesOfTheYear
{
private int totalIncome;
SellProduct sellProduct;
public SalesOfTheYear()
{
totalIncome = 0;
}
public void cashOut(SellProduct sellProduct)
{
totalIncome = sellProduct.getProductCost() + totalIncome;
}
public void cashOut(List<SellProduct> sellProducts)
{
for (SellProduct product : sellProducts){
cashOut(product);
}
}
public int getSalesOfTheYear()
{
return totalIncome;
}
}
How to use :
SalesOfTheYear salesOfTheYear = new SalesOfTheYear();
salesOfTheYear.cashOut(new SellProduct(500));
salesOfTheYear.cashOut(new SellProduct(100));
int totalSale = salesOfTheYear.getSalesOfTheYear();