I recently came across a very stupid (at least from my point of view) implementation inside Androids Parcel class.
Suppose I have a simple class like this
class Foo implements Parcelable{
private String[] bars;
//other members
public in describeContents(){
return 0;
}
public void writeToParcel(Parcel dest, int flags){
dest.writeStringArray(bars);
//parcel others
}
private Foo(Parcel source){
source.readStringArray(bars);
//unparcel other members
}
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>(){
public Foo createFromParcel(Parcel source){
return new Foo(source);
}
public Foo[] newArray(int size){
return new Foo[size];
}
};
}
Now, if I want to Parcel a Foo Object and bars is null I see no way to recover from this situation (exept of catching Exceptions of course). Here is the implementation of these two methods from Parcel:
public final void writeStringArray(String[] val) {
if (val != null) {
int N = val.length;
writeInt(N);
for (int i=0; i<N; i++) {
writeString(val[i]);
}
} else {
writeInt(-1);
}
}
public final void readStringArray(String[] val) {
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
val[i] = readString();
}
} else {
throw new RuntimeException("bad array lengths");
}
}
So writeStringArray is fine if I pass bars which are null. It just writes -1 to the Parcel. But How is the method readStringArray supposed to get used? If I pass bars inside (which of course is null) I will get a NullPointerException from val.length. If I create bars before like say bars = new String[???] I don't get any clue how big it should be. If the size doesn't match what was written inside I recieve a RuntimeException.
Why is readStringArray not aware of a result of -1 which gets written on null objects from writeStringArray and just returns?
The only way I see is to save the size of bars before I call writeStringArray(String[]) which makes this method kind of useless. It will also redundatly save the size of the Array twice (one time for me to remember, the second time from writeStringArray).
Does anyone know how these two methods are supposed to be used, as there is NO java-doc for them on top?
You should use Parcel.createStringArray() in your case.
I can't imagine a proper use-case for Parcel.readStringArray(String[] val) but in order to use it you have to know the exact size of array and manually allocate it.
It's not really clear from the (lack of) documentation but readStringArray() is to be used when the object already knows how to create the string array before calling this function; for example when it's statistically instanciated or it's size is known from another previously read value.
What you need here is to call the function createStringArray() instead.
Related
I have a method whose some parts of are repetitive. I managed to split the code in a way that the original method uses smaller methods many times. The problem is that when I pass some values as params to those new smaller methods and work with them, original values don't get updated. The actions I do are: value incrementation or removing items from arraylist. How do I update the original values using the smaller methods, not only the references? The only possible solution I came up with is to create a new class where I could have a setter for those values and update them. But are there easier/straightforward ways to solve it?
Part of the main method:
int numberOfBlocks = 0;
int currentBlock = 0;
int currentTime = 0;
ArrayList<Request> queue = new ArrayList<Request>();
if(queue.size != 0) {
updateStatus(queue, currentBlock, numberOfBlocks);
}
if(something else happens) {
removeFinished(queue, currentBlock);
}
Smaller methods:
private void updateStatus(ArrayList<Request> queue, int currentBlock, int numberOfBlocks) {
if (queue.get(0).getBlock() > currentBlock)
currentBlock++;
else
currentBlock--;
numberOfBlocks++;
}
private void removeFinished(ArrayList<Request> queue, int currentBlock){
if (queue.get(0).getBlock() == currentBlock) {
queue.remove(0);
}
}
First of all, if you pass a parameter in order for it to be changed in the method, your method should return the changed value, this will resolve your issue. If more then one value needs to be changed, then you are correct, primitives are passed y value and the invoking method doesn't see the changes made in the invoked method. So you should encapsulate those values in a wrapping class with setters and getters like you wanted to. That alone will resolve your problem. But also it would make sense for your method to return that class since it is modified. This is just a good stile
If the smaller methods are in the same class as the main method, simply don't pass them as parameters and the methods will use the class's fields. This is non-thread safe, but your class is non-thread safe already anyway.
class MyClass
{
int numberOfBlocks = 0;
int currentBlock = 0;
int currentTime = 0;
ArrayList<Request> queue = new ArrayList<Request>();
void myMainMethod() {
if(queue.size != 0) {
updateStatus();
}
if(something else happens) {
removeFinished();
}
}
private void updateStatus() {
if (queue.get(0).getBlock() > currentBlock)
currentBlock++;
else
currentBlock--;
numberOfBlocks++;
}
private void removeFinished() {
if (queue.get(0).getBlock() == currentBlock) {
queue.remove(0);
}
}
I'm sitting on an assignment for university and I'm at a point, where I fear I haven't really understood something fundamental in the concecpt of Java or OOP altogether. I'll try to make it as short as possible (maybe it's sufficient to just look at the 3rd code segment, but I just wanted to make sure, I included enough detail). I am to write a little employee management. One class within this project is the employeeManagement itself and this class should possess a method for sorting employees by first letter via bubblesort.
I have written 3 classes for this: The first one is "Employee", which contains a name and an ID (a running number) , getter and setter methods and one method for checking whether the first letter of one employee is smaller (lower in the alphabet) than the other. It looks like this:
static boolean isSmaller(Employee source, Employee target) {
char[] sourceArray = new char[source.name.length()];
char[] targetArray = new char[target.name.length()];
sourceArray = source.name.toCharArray();
targetArray = target.name.toCharArray();
if(sourceArray[0] < targetArray[0])
return true;
else
return false;
}
I tested it and it seems to work for my case. Now there's another class called EmployeeList and it manages the employees via an array of employees ("Employee" objects). The size of this array is determined via constructor. My code looks like this:
public class EmployeeList {
/*attributes*/
private int size;
private Employee[] employeeArray;
/* constructor */
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
}
/* methods */
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
/* adds employee to end of the list. Returns false, if list is too small */
boolean add(Employee m) {
int id = m.getID();
if (id > employeeArray.length) {
return false;
} else {
employeeArray[id] = m;
return true;
}
}
/* returns employee at certain position */
Employee get(int index) {
return employeeArray[index];
}
/* Sets employee at certain position. Returns null, if position doesn't exist. Else returns old value. */
Employee set(int index, Employee m) {
if (employeeArray[index] == null) {
return null;
} else {
Employee before = employeeArray[index];
employeeArray[index] = m;
return before;
}
}
Now comes my real problem: In a third class called "employeeManagement" I am supposed to implement the sorting algorithm. The class looks like this:
public class EmployeeManagement {
private EmployeeList ml = new EmployeeList(3);
public boolean addEmployee(Employee e) {
return ml.add(e);
}
public void sortEmployee() {
System.out.println(ml.getSize()); // I wrote this for debugging, exactly here lies my problem
for (int n = ml.getSize(); n > 1; n--) {
for (int i = 0; i < n - 1; i++) {
if (Employee.isSmaller(ml.get(i), ml.get(i + 1)) == false) {
Employee old = ml.set(i, ml.get(i + 1));
ml.set(i+1, old);
}
}
}
}
The "println" before my comment returns "0" in console... I am expecting "3" as this is the size I gave the "EmployeeList" as parameter of the constructor within my "EmployeeManagement" class. Where is my mistake ? And how can I access the size of the object I created in the "EmployeeManagement" class (the "3") ? I'm really looking forward to your answers!
Thanks,
Phreneticus
You are not storing size in your constructor. Something like,
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
this.size = size; // <-- add this.
}
Also, setSize isn't going to automatically copy (and grow) the array. You will need to copy the array, because Java arrays have a fixed length. Finally, you don't really need size here since employeeArray has a length.
The size variable you are calling is the class field. If you take a quick look at your code, the getter is getting the field (which is initialized as zero when created). The size you are using it. The good way of doing it would be to get the size of the array in the getter like this:
public int getSize() {
return employeeArray.length;
}
This would return the size of the array in the object.
This question already has answers here:
What's the best way to implement `next` and `previous` on an enum type?
(7 answers)
Closed 6 years ago.
I would like to create a method called increaseValue, which it's signature is as following:
public Size increaseValue(Size s)
Also I have the following statement:
protected enum Size {XS, S, M, L, XL}
I need to know, how can I make the method return correct value (i.e. XL when input is L... etc.) while not using Switch-Case statement ?
Thanks !
You could assume they are in increasing ordinal() order. I would add this a method on Size.
protected enum Size {
XS, S, M, L, XL;
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Note: I wouldn't assume that XS is after XL, but rather you get an error (though not a very clear one)
Note: every time you call values() it creates a new array. It has to do this because the array is mutable and you might change it. I highly recommend saving a copy and avoid calling values() each time.
You can make the error messages clearer by overriding those methods.
protected enum Size {
XS {
public Size decrementSize() { throw new UnsupportedOperationException("No smaller size"); }
},
S,
M,
L,
XL {
public Size incrementSize() { throw new UnsupportedOperationException("No larger size"); }
};
static final Size[] VALUES = values();
public Size incrementSize() { return VALUES[ordinal()+1]; }
public Size decrementSize() { return VALUES[ordinal()-1]; }
}
Here's why you should not do it: if you perform arithmetic on your enum, you can end up with invalid values, for instance what would happen if you added one to XL?
Here's how you do it:
Size.values()[s.ordinal()+1]
In good OO design you want to internalize such things. Meaning: you want to provide a method like "nextSize()" within that enum, like:
public enum Size {
XS, ...;
public Size nextSize() {
switch (this) ...
In this situation, the values are probably "fixed"; but in other situations, you might later want to insert new constants; thus I prefer an explicit mapping here; instead of relying on calls to ordinal().
And as mentioned in the other answers: you need to define what largestSize().nextSize() means. It could throw an exception, or return null (baaad idea). Alternatively, that method could return Optional<Size>; to make it clear to the caller that this method doesn't always return a valid result.
public Size increaseValue(Size s) {
Size[] allValues = Size.values();
var newOrdinal = s.ordinal() + 1;
return (newOrdinal >= allValues.length) ? null) : allValues[newOrdinal];
}
public Size decreaseValue(Size s) {
var newOrdinal = s.ordinal() - 1;
return (newOrdinal < 0) ? null : values()[newOrdinal];
}
You van modify this enum like this:
protected enum Size {
XS,(1)
S(2),
M(3),
L(4),
XL(5);
private int sizeNo;
Size(int sizeNo) {this.sizeNo = sizeNo;}
Size getBySizeNo(int sizeNo){
for(size : Size.values()) {
if (sizeNo == size.getSizeNo() ) {
return size;
}
}
throw new IllegalArgumentException() ;
}
public Size increaseValue(Size s){
return getBySizeNo(s.getSizeNo() +1) ;
}
}
Try this:
public Size increaseValue(Size s) {
return Size.values()[s.ordinal() + 1]
}
So lets say I have a class BaseballCard that creates a baseball card.
Now I need to make another class which would be my collection class.
For example I would call it BaseballCardCollection
and then I want to create methods like
size (which returns the numbers of cards in the collection)
addCard(adds a baseball object to the collection object)
removeCard (removes a baseball card)
and so on
What would be the best way to do this. I tried doing this
public CardCollectionList() {
BaseballCard[] baseballCardList = new BaseballCard[101];
}
So each object is insinuated with an array of type BaseballCard of size 100.
And then for example the size method I tried something like this
public int size(){
int size = 0;
for(int i = 1; i<this.baseballCardList.length; i++)
if (baseballCardList!= null)
size+=1;
}
But it doesn't work because "baseballCardList cannot be resolved to a variable"
You could try using ArrayLists - http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html:
ArrayList<baseballCard> baseballCardList = new ArrayList<baseballCard>(0);
public boolean addCard(baseballCard card){
return baseballCardList.add(card);
}
public boolean removeCard(int card){
return baseballCardList.remove(card);
}
public baseballCard getCard(int card){
return baseballCardList.get(card);
}
public int sizeBaseballCardList(){
return baseballCardList.size();
}
public ArrayList<baseballCard> getBaseballCardList(){
return baseballCardList;
}
Move the variable BaseballCard[] baseballCardList outside the constructor, make it a field in your class. Do similar with size.
This is how the class should look like:
public class CardCollectionList {
//fields
private BaseballCard[] baseballCardList;
private int size;
//constructor
public CardCollectionList() {
baseballCardList = new BaseballCard[101];
}
//method
public int getSize() {
return this.size;
}
}
You could try creating your own class implementing the Collection interface and define your own methods + implement Collection methods:
public class myContainer implements Collection <BaseballCard> {
}
You need to move the variable declaration from the constructor to the class, so you can access it in other methods, too.
class CardCollectionList {
BaseballCard[] baseballCardList;
public CardCollectionList() {
baseballCardList = new BaseballCard[101];
}
public int size(){
int size = 0;
for(int i = 1; i<this.baseballCardList.length; i++) {
if (baseballCardList[i] != null) {
size+=1;
}
}
return size;
}
}
The code is as close to your fragment as possible. There are several ways to improve this (keep track of the size when adding, automatic array reallocation etc.). But it is a start if you want to try this yourself.
Normally, you'd probably just use ArrayList<BaseballCard>.
Now I need to make another class which would be my collection class.
... What would be the best way to do this.
I don't have enough reputation to comment on your question, so I am going to assume that you just want to store BaseballCard objects in a Java Collection. The Java SDK offers a lot of options. Since you are asking about the "best" way to go then I would use one of those unless you need to add additional functionality .
if you don't find what you need from the Java SDK or just want to create your own Collection then follow the advice given by #michał-szydłowski above
Ok, here is the code and then the discussion follows:
public class FlatArrayList {
private static ArrayList<TestWrapperObject> probModel = new ArrayList<TestWrapperObject>();
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] currentRow = new int[10];
int counter = 0;
while (true) {
for (int i = 0; i < 10; i++) {
currentRow[i] = probModel.size();
}
TestWrapperObject currentWO = new TestWrapperObject(currentRow);
probModel.add(counter, currentWO);
TestWrapperObject testWO = probModel.get(counter);
// System.out.println(testWO);
counter++;
if (probModel.size() == 10) break;
}
// Output the whole ArrayList
for (TestWrapperObject wo:probModel) {
int [] currentTestRow = wo.getCurrentRow();
}
}
}
public class TestWrapperObject {
private int [] currentRow;
public void setCurrentRow(int [] currentRow) {
this.currentRow = currentRow;
}
public int [] getCurrentRow() {
return this.currentRow;
}
public TestWrapperObject(int [] currentRow) {
this.currentRow = currentRow;
}
}
What is the above code supposed to do? What I am trying to do is load an array as a member of some wrapper object (TestWrapperObject in our case). When I get out of the loop,
the probModel ArrayList has the number of elements it is supposed to have but all have the same value of the last element (an array of size 10 with each item equal to 9). This is not the case inside the loop. If you perform the same "experiment" with a primitive int value everything works fine. Am I missing something myself regarding arrays as object members? Or did I just encounter a Java bug? I am using Java 6.
You are only creating one instance of the currentRow array. Move that inside the row loop and it should behave more like you expect.
Specifically, the assignment in setCurrentRow does not create a copy of the object, but only assigns the reference. So each copy of your wrapper object will hold a reference to the same int[] array. Changing the values in that array will make the values appear to change for all other wrapper objects that hold a reference to the same instance of the array.
i don' t want to sound condescending, but always try to remember tip #26 from the excellent pragmatic programmer book
select isn't broken
it is very rare to find a java bug. keeping this in mind often helps me to look over my code again, turn it around, and shake out the loose bits until i finally discover where i was wrong. of course asking for help early enough is very encouraged, too :)