I have two very simple classes, one extends the other:
public class LocationType implements Parcelable {
protected int locid = -1;
protected String desc = "";
protected String dir = "";
protected double lat = -1000;
protected double lng = -1000;
public LocationType() {}
public int getLocid() {
return locid;
}
public void setLocid(int value) {
this.locid = value;
}
public String getDesc() {
return desc;
}
public void setDesc(String value) {
this.desc = value;
}
public String getDir() {
return dir;
}
public void setDir(String value) {
this.dir = value;
}
public double getLat() {
return lat;
}
public void setLat(double value) {
this.lat = value;
}
public double getLng() {
return lng;
}
public void setLng(double value) {
this.lng = value;
}
// **********************************************
// for implementing Parcelable
// **********************************************
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt (locid);
dest.writeString(desc );
dest.writeString(dir );
dest.writeDouble(lat );
dest.writeDouble(lng );
}
public static final Parcelable.Creator<LocationType> CREATOR = new Parcelable.Creator<LocationType>() {
public LocationType createFromParcel(Parcel in) {
return new LocationType(in);
}
public LocationType[] newArray(int size) {
return new LocationType[size];
}
};
private LocationType(Parcel dest) {
locid = dest.readInt ();
desc = dest.readString();
dir = dest.readString();
lat = dest.readDouble();
lng = dest.readDouble();
}
}
and:
public class MyLocationType extends LocationType {
private ArrayList<ArrivalType> mArrivals = new ArrayList<ArrivalType>();
public List<ArrivalType> getArrivals() {
return mArrivals;
}
public void addArrival(ArrivalType arrival) {
mArrivals.add(arrival);
}
}
The problem is that when I cast an instance of LocationType to MyLocationType I get a ClassCastException. Why is this?
Because LocationType is the superclass; it can't be cast to the subclass.
To explain a bit further: you can only cast up the inheritance tree, which is to say, an object can only be cast as the class type it was created as, any of its superclasses, or any interface that it implements. Thus, a String can be cast as either a String or an Object; a HashMap can be cast as a HashMap, an AbstractMap Map, or an Object.
In your case, a MyLocationType can be either a MyLocationType or a LocationType (or an Object), but not the other way around.
The Java docs on inheritance are pretty good, just to review here.
This is because LocationType is not an instance of MyLocationType it is it's parent. For example if you added new methods to MyLocationType not in LocationType and then cast LocationType to MyLocationType what would expect to happen if these new methods were called that do not exist in the base LocationType?
MyLocationType is a LocationType but LocationType is not a MyLocationType.
Because MyLocationType is of type LocationType and can be cast to one, but LocationType is not of MyLocationType and therefor cannot be cast to it.
LocationType lt = new LocationType();
MyLocationType myLt = new MyLocationType();
LocationType t1 = (LocationType)lt; // OK, but cast not required
LocationType t2 = (LocationType)myLt; // OK, but cast ALSO not required
MyLocationType t3 = (MyLocationType)lt;
// ClassCastException, MyLocationType extends (is-a) LocationType,
// not the other way around
MyLocationType t3 = (MyLocationType)lt;
MyLocationType t5 = (MyLocationType)myLt; // OK, but cast not nessecary
explicit casting down the tree is almost never required, and up the tree is risky:
public void doThis(LocationType lt) {
MyLocationType myLt = (MyLocationType)lt; // DANGEROUS
}
this kind of stuff should only be done if you override a method with a certain
signature, and even then you should check:
#Override
public void doThis(LocationType lt) {
if (lt instanceof MyLocationType) {
MyLocationType myLt = (MyLocationType)lt;
} else {
// A LocationType but not MyLocationType
doSomethingElse(lt);
}
}
Because the reference you're trying to case is not an instance of MyLocationType
This sample always help me to think what's happening.
Object o = new Object();
String s = ( String ) o;
Although String inherits from Object that doesn't mean that every Object is an string ( or in this case, that the "casted" object is an String )
Related
I will give the example java code for this circumstances:
// StateEntity interface define:
public interface StateEntity extends IDEntity { // the IDEntity define getId & setId
static int processBitState(int state, boolean op, int pos) {
if (op) {
state = Bits.set(state, pos);
} else {
state = Bits.clear(state, pos);
}
return state;
}
static <E extends Enum<E>, T extends StateEntity> int gatherState(Class<E> enumType, int state, #NotNull T data) {
try {
Method getJsonFieldName = enumType.getMethod("getJsonFieldName");
Method getPosition = enumType.getMethod("getPosition");
BeanInfo beanInfo = Introspector.getBeanInfo(data.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (E bitSet : EnumSet.allOf(enumType)) {
String fieldName = (String) getJsonFieldName.invoke(bitSet);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
if (propertyDescriptor.getName().equals(fieldName)) {
Method reader = propertyDescriptor.getReadMethod();
Boolean value = (Boolean) reader.invoke(data);
state = processBitState(state, value, (Integer) getPosition.invoke(bitSet));
break;
}
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IntrospectionException e) {
e.printStackTrace();
}
return state;
}
<E extends Enum<E>> Class<E> getStateType();
Integer getState();
void setState(Integer state);
}
// Impl class:
public class StateEntityClass implements StateEntity {
Long id;
Integer state;
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public <E extends Enum<E>> Class<E> getStateType() {
return (Class<E>) (BitSet.class);
}
#Override
public Integer getState() {
return state;
}
#Override
public void setState(Integer state) {
this.state = state;
}
public enum BitSet {
ACTIVATION(0),
FREEZE(1),
UPDATE(2);
private final int position;
BitSet(int v) {
position = v;
}
#NotNull
public String getName() {
return name().toLowerCase().replaceAll("_", "-");
}
#NotNull
public String getJsonFieldName() {
return NameConverter.snakeCaseToCamelCase("is_" + name().toLowerCase());
}
public int getPosition() {
return position;
}
}
}
// used:
public class Main {
public static void main(String[] args) {
StateEntityClass e = new StateEntityClass();
// first method, this statement error:
e.setState(StateEntity.gatherState(e.getStateType(), e.getState() == null ? 0 : e.getState(), e));
// second method, but this statement ok:
e.setState(StateEntity.gatherState(StateEntityClass.BitSet.class, e.getState() == null ? 0 : e.getState(), e));
// why?? how change the first method
}
}
I have some class implements StateEntity, so I must use the first method for generic.
How do I change my code to use the first method?
What is the Java way to process these circumstances problems?
The Enum content is unrelated, the StateEntity implements class has self Enum for the state.
The problem I hope to solve is how to deal with an Enum uniformly in Java. This Enum has a unified structure and a unified interface, but the Enumerator contained is different. See the above question for the specific situation. After #tgdavies' prompt, I moved the type parameter from the method definition to the interface definition, which solved the problem. Later, under the reminder of #Guillaume F., I further optimized the use of Enum. Thank you all for participating
I created a parceable class and wanted to use it for data transfer from fragment A to B. I did it like in many other tutorials but I can't instantiate from parceable object class. it always says, that I have to put in 'Parcel in' as parameter.
Here my object class:
public class DataObject implements Parcelable {
private int number1 = 0;
private int number2 = 0;
private String name = "";
public int getNumber1() {
return number1;
}
public void setNumber1(int number1) {
this.number1 = number1;
}
public int getNumber2() {
return number2;
}
public void setNumber2(int number2) {
this.number2 = number2;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected DataObject(Parcel in) {
number1 = in.readInt();
number2 = in.readInt();
name = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(number1);
dest.writeInt(number2);
dest.writeString(name);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<DataObject> CREATOR = new Parcelable.Creator<DataObject>() {
#Override
public DataObject createFromParcel(Parcel in) {
return new DataObject(in);
}
#Override
public DataObject[] newArray(int size) {
return new DataObject[size];
}
};
}
And here is how I use objects from this type:
DataObject bla = new DataObject();
bla.setNumber1(1);
bla.setNumber2(2);
bla.setName("TestName");
When I hover the red highlighted constructor it says:
DataObject (Parcel) in DataObject cannot be applied
and compiler says:
Error:(30, 50) error: constructor PointCardMainData in class
PointCardMainData cannot be applied to given types;
required: Parcel
found: no arguments
reason: actual and formal argument lists differ in length
Has anybody an idea what is missing? Do I have to set something in the manifest- oder gradle-file? do I have to do something before building the project?
best regards
You've written just one constructor in the DataObject class, and it specifies how to make a DataObject from a Parcel. Now you're trying to make a DataObject without a parcel. You need to either add a Parcel to the line DataObject bla = new DataObject(); or add a new constructor.
So you might write
Parcel theParcel = new Parcel();
DataObject bla = new DataObject(theParcel);
Or you might have a constructor in your DataObject class like
public DataObject(){
}
Add a constructor in your DataObject class
public DataObject()
{}
I have a CustomObject declared as raw type of <T>. And when I populate a List<CustomObject> with new instances of it, I can't get them back as a CustomObject, only as an Object.
public class CustomObject<T> {
private String name;
private T value;
// getters and setters
}
But obviously when I use subclass, all is working as expecting;
public class CustomObject {
private class SubCustomObject<T> {
private String name;
private T value;
}
public CustomObject() {
this.customObject = new SubCustomObject();
private SubCustomObject customObject;
// getters and setters
}
Is there a way to make the first example to behave like the second one, and avoid using extra object and so I could do this:
public class CustomObject<T> {
private String name;
private T value;
private boolean isGroup;
// getters and setters
private void setValue(T value) {
if (value instanceof String) {
this.value = value;
this.isGroup = false;
}
if (value instanceof CustomObject) {
if (isGroup()) {
((List<CustomObject>) this.value).add((CustomObject) value);
} else {
this.value = (T) new ArrayList<CustomObject>();
this.isGroup = true;
setValue(value);
}
}
}
}
public void getItemByName(String name) {
// say the list is already populated
for (CustomObject object : listOfCustomObject) {
String nameField = object.getName();
if (name.equals(nameField) {
System.out.println(nameField);
}
}
}
Instead of this one:
public void getItemByName(String name) {
// say the list is already populated
for (Object object : listOfCustomObject) {
String nameField = ((CustomObject)object).getName();
if (name.equals(nameField) {
System.out.println(nameField);
}
}
}
// Apply that behavior to this and avoid to use inner class.
public class MetadataEntry {
public MetadataEntry() {
this.entity = new Entry();
}
private class Entry<T> {
private String name;
private T value;
private boolean isGroup;
private void setValue(T value) {
if (value instanceof String) {
this.value = value;
this.isGroup = false;
}
if (value instanceof MetadataEntry) {
if (isGroup()) {
((List<MetadataEntry>) this.value).add((MetadataEntry) value);
} else {
this.value = (T) new ArrayList<MetadataEntry>();
this.isGroup = true;
setValue(value);
}
}
}
}
private Entry entity;
public void setName(String name) {
this.entity.name = name;
}
public String getName() {
return this.entity.name;
}
public void setValue(String value) {
entity.setValue(value);
}
public void setValue(MetadataEntry value) {
entity.setValue(value);
}
public boolean isGroup() {
return this.entity.isGroup;
}
public List<MetadataEntity> getChildNodes() {
if (isGroup()) {
return (List<MetadataEntry>) this.entity.value;
}
return null;
}
public String getValue() {
if (!isGroup()) {
return (String) this.entity.value;
}
return null;
}
}
You can not make a list of different types X,Y,Z and put it in a single container of type W. You need to define a bounding parameter on your raw type so that your items and list are of same type. probably your T should be bounded by some interface type or it should extends some class.
Here’s my suggestion. I have abandoned the generics. Instead of just one inner class there is now an abstract inner class with two subclasses, one for groups and one for entries that are not groups. The good news: no cast is necessary anywhere.
public class MetadataEntry {
private String name;
static abstract class Entry {
abstract Entry setValue(String value);
abstract Entry setValue(MetadataEntry value);
abstract boolean isGroup();
abstract List<MetadataEntry> getChildNodes();
abstract String getSimpleValue();
}
static class SimpleEntry extends Entry {
private String value;
public SimpleEntry(String value) {
this.value = value;
}
#Override
Entry setValue(String value) {
this.value = value;
return this;
}
#Override
Entry setValue(MetadataEntry value) {
return new GroupEntry(value);
}
#Override
public boolean isGroup() {
return false;
}
#Override
public List<MetadataEntry> getChildNodes() {
return null;
}
#Override
public String getSimpleValue() {
return value;
}
}
static class GroupEntry extends Entry {
List<MetadataEntry> value;
public GroupEntry(MetadataEntry value) {
this.value = new ArrayList<>();
this.value.add(value);
}
#Override
Entry setValue(String value) {
return new SimpleEntry(value);
}
#Override
Entry setValue(MetadataEntry value) {
this.value.add(value);
return this;
}
#Override
public boolean isGroup() {
return true;
}
#Override
public List<MetadataEntry> getChildNodes() {
return value;
}
#Override
public String getSimpleValue() {
return null;
}
}
private Entry entity;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setValue(String value) {
entity = entity.setValue(value);
}
public void setValue(MetadataEntry value) {
entity = entity.setValue(value);
}
public boolean isGroup() {
return this.entity.isGroup();
}
public List<MetadataEntry> getChildNodes() {
return entity.getChildNodes();
}
public String getValue() {
return entity.getSimpleValue();
}
}
I have used an idea similar to what m 1987 said about a class that returns an instance of itself. I applied it to the inner classes only to free the users of the outer class from caring about this trickery. If you prefer, I am sure it could be applied to the outer class instead. Then you would have an abstrat class on the outer level with two subclasses, and would no longer need the inner classes. This is one of the things you asked for, so you may prefer it, but it comes at a cost: anyone calling setValue() on the outer class would need to remember that they got a new instance back.
I have a CustomObject declared as raw type of <T>.
That doesn't makes sense. You either have a raw type or a generic, not a raw type of a generic.
And when I populate a List with new instances of it, I can't get them back as a CustomObject, only as an Object.
Because your list is not generic (always bad). When you declare a List<Something> it will return Something on a get call. That Something can be generic or a raw type. A List<CustomObject<String>> will not accept a CustomObject<Integer> and using the raw type List<CustomObject> will end in disaster, hence the danger in raw types.
Now let's look at your code. The class
public class CustomObject<T> {
private String name;
private T value;
}
defines an object that behaves the same for any type. In essence what you have here is just a glorified Object with a String serving as its name attached to it.
However, now you do
private void setValue(T value) {
if (value instanceof String)
// ...
if (value instanceof CustomObject)
// ...
}
which separates the behavior for different types. and what happens if the generic type is not a String or a CustomObject?
Let's try to solve your problem. Since generics are meant to unify the behavior, let's look at what the unified behavior is that you're trying to get:
public void getItemByName(String name) {
for (CustomObject object : listOfCustomObject) {
String nameField = object.getName();
// ...
}
}
}
Basically, you require that all the items in listOfCustomObject implement a String getName() method. That's it as far as I can see from your question. That means that your CustomObject<T> should either implement an interface or extend a class (call it Superthing) with that method. Then you will just declare your list as List<? extends Superthing>.
As for the CustomObject itself, it doesn't need to be generic as you hint that there are only 2 types of generics you want to deal with (you have 2 ifs, but no else to deal with a general case). It looks like what you want are 2 different classes with different behaviors that both implement or extend a common supertype.
Try something like this:
abstract class AbstractEntry {
private String name;
protected boolean isGroup;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isGroup() {
return isGroup;
}
}
class MetaEntry extends AbstractEntry {
AbstractEntry value;
MetaEntry(AbstractEntry value) {
this.value = value;
// handle isGroup
}
public void setValue(AbstractEntry value) {
this.value = value;
}
public AbstractEntry getValue() {
if (!isGroup)
return value;
return null;
}
}
class StringEntry extends AbstractEntry {
String value;
StringEntry(String value) {
this.value = value;
isGroup = false;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
I think there is no need of list as it always hold only one element. As #Ole V.V mentioned, the requirement naturally calls for the use of composition and in fact, generic does not fit into your requirements. Here is how I would tackle your requirements:
public interface Named {
public String getName();
public String getValue();
}
public class CustomObject implements Named {
private String name;
private String value;
private boolean isGroup;
// getters and setters
private boolean isGroup() {
return isGroup;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class CustomObject2 implements Named {
private String name;
private CustomObject value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value.getValue();
}
public void setValue(CustomObject value) {
this.value = value;
}
}
public class DriverCustomObject {
public static void main(String arg[]) {
CustomObject t = new CustomObject();
t.setName("key1");
t.setValue("value1");
CustomObject2 t2 = new CustomObject2();
t2.setName("complex");
t2.setValue(t);
List<Named> list = new ArrayList<Named>();
list.add(t);
list.add(t2);
for (Named l : list) {
System.out.println(l.getName());
System.out.println(l.getValue());
}
}
}
I'm having an issue with a self-bounding generic type which has a self-bounding generic subtype.
I'm trying to implement some kind of builder pattern and i'd like to have my statements more or less like in the main method.
Can anyone help me out in finding a better why to declare the generics so I no longer need the cast and I don't get compilation errors in the statements. Or can anyone explain in clear text why this can't work?
import java.util.Date;
public class SelfBoundingGenericTypeTest {
public static void main(String[] args) {
ConcreteType type = new ConcreteType().pageSize(1).id(12);
SubType type2 = (SubType) new SubType().id(10).pageSize(0); // Why do i need the cast?
SubType type3 = new SubType().pageSize(0).id(10); // Compile error
}
}
abstract class SuperType<E extends SuperType<E>> {
private int _pageSize = Integer.MIN_VALUE;
private int _startIndex = Integer.MIN_VALUE;
#SuppressWarnings("unchecked")
public E pageSize(int value) {
this._pageSize = value;
return (E) this;
}
#SuppressWarnings("unchecked")
public E startIndex(int value) {
this._startIndex = value;
return (E) this;
}
public int getPageSize() {
return _pageSize;
}
public int getStartIndex() {
return _startIndex;
}
}
class SubType<E extends SubType<E>> extends SuperType<E> {
private long _id = Long.MIN_VALUE;
#SuppressWarnings("unchecked")
public E id(long value) {
this._id = value;
return (E) this;
}
public long getId() {
return _id;
}
}
class ConcreteType extends SubType<ConcreteType> {
private Date _startDate;
public Date getStartDate() {
return _startDate;
}
public ConcreteType startDate(Date value) {
this._startDate = value;
return this;
}
}
You need the cast because SubType is a raw type. As such all its members are raw, including those inherited from SuperType. The raw signature of SuperType.pageSize is its erasure SuperType pageSize(int). So the "fix" is to not use raw types. This will all magically work for ConcreteType.
Edit: Don't use raw types. Ever. You should use your ConcreteType, but before you use the stupid, moronic, idiotic "solution" of redeclaring every method, use ((SubType<?>) new SubType()) instead.
I am not really sure about the failing reason but that's my understanding:
abstract class SuperType> {
public E pageSize(int value) { ...}
}
For method pageSize, as you have E declared as extends SuperType<E>, after type-erasure, the method signature is in fact SuperType pageSize(int) , which caused the problem in new SubType().pageSize(0).id(10) because pageSize is returning a SuperType.
Though it doesn't looks as magical as you expect, with covariant return type you can simply "overload" those methods in inherited class:
import java.util.Date;
public class SelfBoundingGenericTypeTest {
public static void main(String[] args) {
ConcreteType type = new ConcreteType().pageSize(1).id(12);
SubType type2 = new SubType().id(10).pageSize(0); // works fine now
SubType type3 = new SubType().pageSize(0).id(10); // works fine too
}
}
abstract class SuperType {
private int _pageSize = Integer.MIN_VALUE;
private int _startIndex = Integer.MIN_VALUE;
public SuperType pageSize(int value) {
this._pageSize = value;
return this;
}
public SuperType startIndex(int value) {
this._startIndex = value;
return this;
}
public int getPageSize() {
return _pageSize;
}
public int getStartIndex() {
return _startIndex;
}
}
class SubType extends SuperType {
private long _id = Long.MIN_VALUE;
public SubType id(long value) {
this._id = value;
return this;
}
public SubType pageSize(int value) {
return (SubType) super.pageSize(value);
}
public SuperType startIndex(int value) {
return (SubType) super.pageSize(value);
}
public long getId() {
return _id;
}
}
class ConcreteType extends SubType {
private Date _startDate;
public Date getStartDate() {
return _startDate;
}
public ConcreteType startDate(Date value) {
this._startDate = value;
return this;
}
public ConcreteType id(long value) {
return (ConcreteType) super.id(value);
}
public ConcreteType pageSize(int value) {
return (ConcreteType) super.pageSize(value);
}
public ConcreteType startIndex(int value) {
return (ConcreteType) super.pageSize(value);
}
}
private class FileType extends Object {
private String Name;
private String Type;
public FileType() {
Name = null;
Type = null;
}
public void setFileType(String n, String t) {
Name = n;
Type = t;
}
public int compareTo(FileType ft) {
String decodedFileName = null;
String decodedInputName = null;
try {
decodedFileName = URLDecoder.decode(this.Name, "UTF-8");
decodedInputName = URLDecoder.decode(ft.Name, "UTF-8");
}
catch(UnsupportedEncodingException e) {
}
return decodedFileName.compareToIgnoreCase(decodedInputName);
}
}
Above code is my define class for file list.
I had implement compare file name.
The type may Folder or File.
But I want to sort file first priority is Type, and second priority is Name.
How can arrive it?
You have to implement Comparable / compareTo method.
Use Comparator Interface from java.util package to sort in more than one way......
Use the compare(T t1, T t2) method of Comparator
Eg:
Here is an example from site http://www.mkyong.com
import java.util.Comparator;
public class Fruit{
private String fruitName;
private String fruitDesc;
private int quantity;
public Fruit(String fruitName, String fruitDesc, int quantity) {
super();
this.fruitName = fruitName;
this.fruitDesc = fruitDesc;
this.quantity = quantity;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getFruitDesc() {
return fruitDesc;
}
public void setFruitDesc(String fruitDesc) {
this.fruitDesc = fruitDesc;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public static Comparator<Fruit> FruitNameComparator
= new Comparator<Fruit>() {
public int compare(Fruit fruit1, Fruit fruit2) {
String fruitName1 = fruit1.getFruitName().toUpperCase();
String fruitName2 = fruit2.getFruitName().toUpperCase();
//ascending order
return fruitName1.compareTo(fruitName2);
//descending order
//return fruitName2.compareTo(fruitName1);
}
};
}
Compare both types. If the comparison is different from 0, return it as a result. If equal to 0, then compare the names.
Note that:
Extending Object is unnecessary: it's the default
Fields should start with a lower-case letter: name, type na dnot Name, Type
Your class should implement Comparable<FileType>
I would choose another name for the class: It's not a file type, but a file name associated to a file type
I would use an enum rather than a String for file types, since you only have two valid instances of file types
You should never ignore exceptions like you're doing. BTW, if this exception occurs, it could lead to a NullPointerException. Wrap the exception into a runtime exception and throw this runtime exception:
catch(UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
Your compareTo method doesn't handle null names, although the default constructor assigns null to the name. Fix the method or the constructor. It seems to me that a file name should never be null, so I would fix the constructor.
if (this.Type.equals(ft.Type)){
return decodedFileName.compareTo(decodedInputName);
}
else{
return this.Type.compareTo(ft.Type);
}
You first compare the type and then the decoded name.
I cached the decodedFileName value directly in the class to prevent calling URLDecoder.decode too much.
private class FileType extends Object implements Comparable<FileType>{
private String name;
private String decodedFileName;
private String type;
public FileType(String n, String t) {
name = n;
try {
decodedFileName = URLDecoder.decode(this.name, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
type = t;
}
public int compareTo(FileType other) {
int result = type.compareToIgnoreCase(other.type);
if (result == 0){
result = decodedFileName.compareToIgnoreCase(other.decodedFileName);
}
return result;
}
}
if you have only two types, why dont make them enum?
then first you compare type.ordinal, if there are equal then compare names,
and also prevents from putting unwanted values there