For my project, I need a 2D array that can hold multiple different object types. The problem with java is that it doesn't let you do that. You can only have an array of a particular object type.
Now don't get me wrong, but I did some research, and one proposed solution is to use an Object array, since every class in java extends the object class by default. For example:
Object[][] someArray = new Object[5][5];
someArray[1][2] = new Cat();
someArray[3][4] = new Dog();
The problem with this is that the object class, being a superclass, cannot access the methods and fields of the subclasses that extend it. So if I have a public eat() method in my cat or dog class, the elements in someArray wont be able to access that method.
So I'm at a dead-end here. Any help is appreciated. Thanks in advance.
[ please don't judge me for answering my own question ]
One possible solution is using 2D ArrayLists. The way a 2D Array works is by literally having a 1D array consisting of 1D arrays.
Maybe the same concept can be applied to ArrayLists, which can in fact store any object types without having accessibility issues. A 5x5 ArrayList can be created as follows:
ArrayList<ArrayList> someArray = new ArrayList<ArrayList>();
for(int x=0 ; x<5 ; x++)
{
someArray.add(new ArrayList());
for(int y=0 ; y<5 ; y++)
someArray.get(x).add(null);
}
and to set row 1 col 2 to a cat object:
someArray.get(1).set(2, new Cat());
someArray.get(3).set(4, new Dog());
So 2D ArrayLists, though a bit confusing might solve this issue pretty efficiently.
to call the eat() method of the Dog class and `Cat`` class, You can cast the array reference to a subclass reference.((Dog)someArray[x][y]).eat();
public class Dog{
public Dog(){
super();
}
public String eat(){
String str = "yum";
return str;
}
public static void main (String[]args){
Object [][] arr = new Object [2] [2];
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr[i].length; j++){
arr[i][j] = new Dog();
}
}
System.out.println(((Dog)arr[1][1]).eat());
}
}
The ideal use in your case is to create a superclass called Animal that is extended by dog and cat. Your ArrayList or array will be of Animal type.
How different do these objects need to be? If they're your own classes, then just create some sort of superclass that they all extend and use that as the array type. If you don't want to do that, I guess you could set something up like this:
private Object[][] array = new Object[5][5];
public void setValue(int x, int y, Object val) {
if(x > 0 && x < array.length && y > 0 && y < array[0].length) {
array[x][y] = val;
}
}
public Object getValue(int x, int y) {
if(x > 0 && x < array.length && y > 0 && y < array[0].length) {
return array[x][y];
}
return null;
}
Using it would be a little tedious, but you could cast each call to getValue() to the type you needed it to be. With Object Oriented programming, having a simple solution to this can be kind of difficult. I could write some more advanced code that would return the object casted to a certain class, but it's a little bit of work. Let me know if you'd like that.
Related
I want to know how do you access an attribute in a class inside an array like an example below:
import java.util.*;
public class DogTest{
public class Dog {
int Quantity;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
Dog dogs[] = new Dog[15];
for ( int i = 1; i <=15; i++){
System.out.println("Enter number of Dogs ");
dogs[i].Quantity = scan.nextInt();
}
}
}
The code above does not seem to work. dogs[i].Quantity is derived from my C++ knowledge by the way.
Error msg:
Exception in thread "main" java.lang.NullPointerException
Is my structure wrong? Or there is another way to do it?
Arrays start at position 0. So at end of your loop you try to access dogs[15] which does not exist. Essentially an array of size 15 is accessed by numbers 0-14. This may be the problem.
Try starting loop like this
for(int i=0;i<15;i++)
{
}
First of all declare a class for itself, not as an inner class like you did.Never give fields first uppercase letter, that is naming convention.
public class Dog{
int quantity;
}
And, your actual problem is that when you declare an array of dogs, you declared an array of size, in your case, 15 but it doesn't contain any objects. You just initialised and array which holds 15 nulls and can be filled with Dog objects. And because that you get a null pointer exception.So, first you should fill your array with the dog objects, something like this:
for (int i = 0; i < dogs.length; i++){
dogs[i] = new Dog(); // calls a constructor for Dog object
}
And, after that, you can access your objects trough for loop to change a field quantity
for(int i = 0; i < dogs.length; i++){
dogs[i].quantity = i;
}
Also, I would recommend to make your fields private and make getter and setter methods for accessing and changing their value.
Edit: And yes, mikekane was right about the array size, you would get an ArrayIndexOutOfBoundsException just after you fix this problem with the code you've tried to solve it...
I am currently working on a project and I have an array of Objects. I know that the array will store the following types: Product, Food, Clothes (food and clothes are both subclasses of Product). The thing is that I am trying to access an attribute of an object from the array (barCode), but when I try to do that it gives me an error and says that "barCode is undefined for type Object".
How can I solve this problem?
I cannot create an separates array for every type because it won't be scalable when I would add more classes and types.
Thanks!
yes. the future classes will also be sublcasses of Product
this is one instance of the code. the only problem is on the lines with getBarCode() (which is a method for the Product class)
private Object arr[] = super.getArray();
public void sort(int c)
{
Object aux;
int min = 999, poz;
for(int i = 0; i < super.getIndex() - 1; i ++)
{
for(int j = i; j < super.getIndex(); j ++)
{
if( arr[j].getBarCode() < min)
{
min = arr[j].getBarCode();
poz = j;
}
}
aux = arr[i];
arr[i] = arr[poz];
arr[poz] = aux;
}
}
If you could guarantee that the method barCode exists on Product and its children, then you could just use an array of Product instead.
You would have to deal with any sort of fiddly casting you would want to do if you want to use specific child class methods, but that would at least let you call the same method on all elements without too much worrying.
So long as getArray can be changed to return Product, then you can write this:
private Product[] arr = super.getArray();
You'd change aux to a type of Product, too.
When accessing the object from the array, what you can do is compare the object type.
Like this:
Object o = array[0];
if(o.getClass().equals(Product.class)) {
Product p = (Product)o;
p.getBarcode();
}
Alternatively, if all of your objects you are putting into this array are subclasses of Product, just make an arrayList to use Products, like new ArrayList(), and then call the barcode method for there.
Lets say we have these two different constructors.
What is the different between the first one and the second one.
How is the way to do it? Explain the difference please!
(I know you cant have these two constructors in the same class, this is just to show what i mean.
public class StackOverFlow {
private int[] x; // instance variable
StackOverFlow(int[] x) { // constructor
this.x=x;
}
StackOverFlow(int[] x) { // constructor
this.x = new int[x.length];
for(int k=0 ; k < x.length; k++) {
this.x[k]=x[k];
}
}
The first constructor assigns a reference of an existing int array to the member variable. The caller of the constructor can later change the array and the change would be reflected in the instance.
The second constructor copies the array, so later changes in the passed array wouldn't change the copy stored in the instance.
int[] intArray = new intArray {1,2,3};
StackOverFlow so1 = new StackOverFlow(intArray); // assume we are using the first constructor
intArray[1]=5; // changes the array stored in `so1` object
StackOverFlow so2 = new StackOverFlow(intArray); // assume we are using the second constructor
intArray[1]=8; // doesn't change the array stored in `so2` object
In the first case you tell your instance variable to refer to the given x, so when you change data in one of these variables, that changes also affect the second variable.
And in the second case you create a copy of an object, so your instance variable and variable you pass to constructor will need independent from each other in your further code.
This will not work since you got an ambiguity issue as both constructors receive the same type of parameters. So when you try to create an instance :
StackOverflow instance = new StackOverflow(new int[]{});
There is no way to know which constructor should be called.
You need to decide which behavior is good for you.
I would recommend using the second constructor and create a setter method if you want to set the array from an initialized one :
public class StackOverFlow {
private int[] x; // instance variable
StackOverFlow(int[] x) { // conctructor
this.x = new int[x.length];
for(int k=0 ; k < x.length; k++) {
this.x[k]=x[k];
}
}
public void setTheArray(int[] x) {
this.x = x;
}
}
[Background note: I am a new Java programmer with a C++ background, so is a little confused about how arguments are passed around in Java.]
While reading and writing some code, I came to the following case
public class B{
int[] intArr;
Vector<String> strVec;
void mutator(){
// modifies intArr and strVec
}
}
public class A{
B bOfA;
A(B b){
bOfA = b;
}
void foo(){
bofA.mutator();
}
}
foo() in A definitely modifies bOfA, but what about b, the object that is passed in? Will its fields/data members be modified as well?
Are the fields shallow or deep copied?
Are strVec and intArr treated differently, because strVec is a container and inArr is an array, which might be implemented as some kind of pointers, therefore behave quite differently depending on if it is shallow or deep copied.
Thank you.
A (late) update with real code and somewhat surprising (I was assuming the pass-by-value mechanism interpreted in the C/C++ way) results:
import java.util.Vector;
public class B{
int[] intArr = null;
Vector<String> strVec = null;
int x = 0;
String s = null;
B(int sz){
x = 0;
s = new String("ini");
intArr = new int[sz];
strVec = new Vector<String>(sz);
for (int i=0; i<sz; i++){
intArr[i] = 0;
strVec.add( new String("xx") );
}
}
void mutator(){
x = -1;
s = new String("mute");
int sz = intArr.length;
strVec = new Vector<String>(sz);
for (int i=0; i<sz; i++){
intArr[i] = -1;
strVec.add( new String("aa") );
}
}
}
import java.util.Vector;
public class A{
B bOfA=null;
A(B b){
bOfA = b;
}
void foo(){
bOfA.mutator();
}
}
import java.util.Vector;
public class C{
public static void main(String[] args){
B b = new B(3);
A a = new A(b);
System.out.println("Contents of B before:");
System.out.println(b.x);
System.out.println(b.s);
for(int i=0; i<3; i++){
System.out.println(b.intArr[i]);
System.out.println(b.strVec.elementAt(i));
}
a.foo();
System.out.println("\n\nContents of A:");
System.out.println(a.bOfA.x);
System.out.println(a.bOfA.s);
for(int i=0; i<3; i++){
System.out.println(a.bOfA.intArr[i]);
System.out.println(a.bOfA.strVec.elementAt(i));
}
System.out.println("\n\nContents of B after:");
System.out.println(b.x);
System.out.println(b.s);
for(int i=0; i<3; i++){
System.out.println(b.intArr[i]);
System.out.println(b.strVec.elementAt(i));
}
}
}
And the results by cygwin:
Contents of B before:
0
ini
0
xx
0
xx
0
xx
Contents of A:
-1
mute
-1
aa
-1
aa
-1
aa
Contents of B after:
-1
mute
-1
aa
-1
aa
-1
aa
Java passes everything by value. But, the value of Object(s) are references.
Both the array and the Vector are shallow copies.
Arrays are reference objects. When you assign them, they are not copied at all - what happens there is similar to assigning an array to a pointer in C++.
When you copy an array using System.arraycopy, a shallow copy is performed. Constructing a collection from another collection creates a shallow copy as well.
Note: Unlike C++ libraries, Java class libraries uses immutable classes a lot. For example, String is immutable; so are the wrappers for numbers, such as Integer. Immutability makes the shallow vs. deep copying a lot less relevant.
Another note: Vector<T> should be used when you need a synchronized container; if you do not want synchronization, use ArrayList<T> instead.
what about b, the object that is passed in?
Will its fields/data members be modified as well?
Since java by default is pass-by-value, object that is passed in the contructor is pass-by-value but its internally its fields within that object will have reference, therefore when you call bofA.mutator() the B b field will change as well.
To have deep copy you need to copy each of the field in the b object that was passed and use Arrays.copyOf().
Imagine, if you will, a 10x10x10 cube made out of 1x1x1 bricks. Each brick must be accessable by an x,y,z coordinate. For each brick I also need to store a list of names of who own that 'brick'.
As efficiency is an absolute must, I came up with the following idea - A 3d array of vectors.
note- I have made a class which stores a name, and other info (called person)
//declaration
protected Vector<person>[][][] position;
I think I must then allocate memory to the pointer position. I have tried this
position = new Vector<person>[10][10][10];
But am getting an error 'Cannot create a generic array of Vector' I am only familiar with C++ and Java is new to me. I understand java does not like declaring arrays with generic type? Does anyone know how I can get around this problem?
Cheers
No need to complicate things that much! You know the size of the array (10:10:10), so there's no need to go for vectors or other stuff for the bricks. Try using array of objects:
Class Brick {
public Brick(int x, int y, int z){
this.x=x;
this.y=y;
this.z=z;
owners = new ArrayList <String> ();
}
List<String> owners;
int x, y, z; //every brick "knows" its position - you might not need it
}
Code for creating the array:
Public Class Main {
.....
Brick Cube[][][] = new Brick[10][10][10];
for (int x=0; x < 10; x++)
for(int y=0; y < 10; y++)
for(int z=0; z < 10; z++)
{
Cube[x][y][z] = new Brick(x, y, z);
}
//adding an owner to a brick:
Cube[0][0][0].owners.add("Owner");
.....
}
Keep OOP in mind - It makes things much easier!
TODO: add getters/setters
If you did want to go down the route of using a List structure rather than arrays, this should be enough to get you started. This is based off of what #FrustratedWithFormsDes said, but I included the initilization code since it's hard to get the syntax right.
public class Person {
}
class PeopleStorage {
ArrayList<ArrayList<ArrayList<Person>>> data;
public PeopleStorage(int size) {
this.data = new ArrayList<ArrayList<ArrayList<Person>>>(size);
for (int i = 0; i < size; i++) {
ArrayList<ArrayList<Person>> inner = new ArrayList<ArrayList<Person>>(
size);
data.add(inner);
for (int j = 0; j < size; j++) {
ArrayList<Person> inner2 = new ArrayList<Person>(size);
inner.add(inner2);
for (int k = 0; k < size; k++) {
inner2.add(new Person());
}
}
}
}
public Person get(int index1, int index2, int index3)
{
//check indices against size, throw IllegalArgumentException here
return data.get(index1).get(index2).get(index3);
}
public void set(Person person, int index1, int index2, int index3)
{
//check indices against size, throw IllegalArgumentException here
data.get(index1).get(index2).set(index3, person);
}
}
What you are trying to do is impossible in java, because there is no operator overloading (like in C++). In short the [] operator in Java is defined only for arrays and cannot be overloaded. Therefore the only way to access elements inside Vector/ArrayList is through the get() method (or through an Iterator or couple other methods, but you get the picture). Analogically you cannot define multi-dimensional ArrayList that way. What you should do is
ArrayList<ArrayList<ArrayList<Person>>> people = new ArrayList<ArrayList<ArrayList<Person>>>()
and then go and initialize all the internal arrays with nested loops or whatever you wish.
It looks a little bit ugly, and since ArrayList/Vector is a dynamic collection adding for example a new element at the first array would require you to initialize the two nested arrays as well. So you might be better off in design terms with writing some form of a wrapper for that class in order to isolate that logic there.
The difference between ArrayList and Vector in Java is that vector is synchronized (therefore slower) and in the default growth pattern (Vector doubles its size, while ArrayList grows by 50%). Otherwise they are pretty much identical in functionality and complexity of operations.
I think you will want to use an ArrayList instead of a vector. I'm a bit rusty on this, but from what I recall, mixing arrays and generic containers is not a good idea.
You could try this:
position = new ArrayList(10)<ArrayList(10)<ArrayList(10)<Person>>;
but it's rather ugly to read. Accessing data is like this:
person = position.get(1).get(3).get(6); //get someone at x=1, y=3, z=6
Beware I don't have the chance right now to compile this and see if it actually works, so you will want to read the docs on ArrayList but I think this is the direction you could go in...
Update: jhominal has pointed out some news that I forgot, I don't have a Java compiler on hand to verify this, but if you try this solution, take a look at what he says as well.
Note: This is more of an explanation of why generic arrays don't work in Java rather than an answer to your actual question.
Effective Java 2nd Edition deals with generic arrays on page 119 of Chapter 5 (PDF).
Why is it illegal to create a generic
array? Because it isn't typesafe. If
it were legal, casts generated by the
compiler in an otherwise correct
program could fail at runtime with a
ClassCastException. This would
violate the fundamental guarantee
provided by the generic type system.
This is because Java Generics only have types at compile time and insert the appropriate casts so specific operations work at runtime.
The easiest solution to this problem is likely the one Ed.C provided.
I would also go with the object answer by Ed.C, but this seems to work:
#SuppressWarnings("unchecked")
protected Vector<Person>[][][] position = new Vector[10][10][10];
ie, you skip the diamond on the right.