I added an external dependency (open CSV) to my Gradle project. I wanted to override a particular method from that.
I subclassed the required class in a new custom class
But I can not override any methods in the super class. Even though they are public
What will be the reason for this.? The Gradle dependencies exist in Library/Project and External Dependencies.
class CustomMappingStrategy<T> extends ColumnPositionMappingStrategy<T> {
private static final String[] HEADER = new String[]{"TradeID", "GWML GUID", "MXML GUID", "GWML File", "MxML File", "MxML Counterparty", "GWML Counterparty"};
#Override //This is not working
public String[] generateHeader() {
return HEADER;
}
}
The super class looks like:
package com.opencsv.bean;
import com.opencsv.CSVReader;
import com.opencsv.ICSVParser;
import com.opencsv.exceptions.CsvBadConverterException;
import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
/**
* Allows for the mapping of columns with their positions. Using this strategy
* without annotations ({#link com.opencsv.bean.CsvBindByPosition} or
* {#link com.opencsv.bean.CsvCustomBindByPosition}) requires all the columns
* to be present in the CSV file and for them to be in a particular order. Using
* annotations allows one to specify arbitrary zero-based column numbers for
* each bean member variable to be filled. Also this strategy requires that the
* file does NOT have a header. That said, the main use of this strategy is
* files that do not have headers.
*
* #param <T> Type of object that is being processed.
*/
public class ColumnPositionMappingStrategy<T> extends AbstractMappingStrategy<String, Integer, ComplexFieldMapEntry<String, Integer, T>, T> {
/**
* Whether the user has programmatically set the map from column positions
* to field names.
*/
private boolean columnsExplicitlySet = false;
/**
* The map from column position to {#link BeanField}.
*/
private FieldMapByPosition<T> fieldMap;
/**
* Holds a {#link java.util.Comparator} to sort columns on writing.
*/
private Comparator<Integer> writeOrder;
/**
* Used to store a mapping from presumed input column index to desired
* output column index, as determined by applying {#link #writeOrder}.
*/
private Integer[] columnIndexForWriting = null;
/**
* Default constructor.
*/
public ColumnPositionMappingStrategy() {
}
/**
* There is no header per se for this mapping strategy, but this method
* checks the first line to determine how many fields are present and
* adjusts its field map accordingly.
*/
// The rest of the Javadoc is inherited
#Override
public void captureHeader(CSVReader reader) throws IOException {
// Validation
if (type == null) {
throw new IllegalStateException(ResourceBundle
.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale)
.getString("type.unset"));
}
String[] firstLine = reader.peek();
fieldMap.setMaxIndex(firstLine.length - 1);
if (!columnsExplicitlySet) {
headerIndex.clear();
for (FieldMapByPositionEntry entry : fieldMap) {
Field f = entry.getField().getField();
if (f.getAnnotation(CsvCustomBindByPosition.class) != null
|| f.getAnnotation(CsvBindAndSplitByPosition.class) != null
|| f.getAnnotation(CsvBindAndJoinByPosition.class) != null
|| f.getAnnotation(CsvBindByPosition.class) != null) {
headerIndex.put(entry.getPosition(), f.getName().toUpperCase().trim());
}
}
}
}
/**
* #return {#inheritDoc} For this mapping strategy, it's simply
* {#code index} wrapped as an {#link java.lang.Integer}.
*/
// The rest of the Javadoc is inherited
#Override
protected Object chooseMultivaluedFieldIndexFromHeaderIndex(int index) {
return Integer.valueOf(index);
}
#Override
public BeanField<T> findField(int col) {
// If we have a mapping for changing the order of the columns on
// writing, be sure to use it.
if (columnIndexForWriting != null) {
return col < columnIndexForWriting.length ? fieldMap.get(columnIndexForWriting[col]) : null;
}
return fieldMap.get(col);
}
/**
* This method returns an empty array.
* The column position mapping strategy assumes that there is no header, and
* thus it also does not write one, accordingly.
*
* #return An empty array
*/
// The rest of the Javadoc is inherited
#Override
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
String[] h = super.generateHeader(bean);
columnIndexForWriting = new Integer[h.length];
// Once we support Java 8, this might be nicer with Arrays.parallelSetAll().
for (int i = 0; i < columnIndexForWriting.length; i++) {
columnIndexForWriting[i] = i;
}
// Create the mapping for input column index to output column index.
Arrays.sort(columnIndexForWriting, writeOrder);
return ArrayUtils.EMPTY_STRING_ARRAY;
}
/**
* Gets a column name.
*
* #param col Position of the column.
* #return Column name or null if col > number of mappings.
*/
#Override
public String getColumnName(int col) {
return headerIndex.getByPosition(col);
}
/**
* Retrieves the column mappings.
*
* #return String array with the column mappings.
*/
public String[] getColumnMapping() {
return headerIndex.getHeaderIndex();
}
/**
* Setter for the column mapping.
* This mapping is for reading. Use of this method in conjunction with
* writing is undefined.
*
* #param columnMapping Column names to be mapped.
*/
public void setColumnMapping(String... columnMapping) {
if (columnMapping != null) {
headerIndex.initializeHeaderIndex(columnMapping);
} else {
headerIndex.clear();
}
columnsExplicitlySet = true;
}
#Override
protected void loadFieldMap() throws CsvBadConverterException {
boolean required;
fieldMap = new FieldMapByPosition<>(errorLocale);
fieldMap.setColumnOrderOnWrite(writeOrder);
for (Field field : loadFields(getType())) {
String fieldLocale, capture, format;
// Custom converters always have precedence.
if (field.isAnnotationPresent(CsvCustomBindByPosition.class)) {
CsvCustomBindByPosition annotation = field
.getAnnotation(CsvCustomBindByPosition.class);
Class<? extends AbstractBeanField> converter = annotation.converter();
BeanField<T> bean = instantiateCustomConverter(converter);
bean.setField(field);
required = annotation.required();
bean.setRequired(required);
fieldMap.put(annotation.position(), bean);
}
// Then check for a collection
else if (field.isAnnotationPresent(CsvBindAndSplitByPosition.class)) {
CsvBindAndSplitByPosition annotation = field.getAnnotation(CsvBindAndSplitByPosition.class);
required = annotation.required();
fieldLocale = annotation.locale();
String splitOn = annotation.splitOn();
String writeDelimiter = annotation.writeDelimiter();
Class<? extends Collection> collectionType = annotation.collectionType();
Class<?> elementType = annotation.elementType();
Class<? extends AbstractCsvConverter> splitConverter = annotation.converter();
capture = annotation.capture();
format = annotation.format();
CsvConverter converter = determineConverter(field, elementType, fieldLocale, splitConverter);
fieldMap.put(annotation.position(), new BeanFieldSplit<T>(
field, required, errorLocale, converter, splitOn,
writeDelimiter, collectionType, capture, format));
}
// Then check for a multi-column annotation
else if (field.isAnnotationPresent(CsvBindAndJoinByPosition.class)) {
CsvBindAndJoinByPosition annotation = field.getAnnotation(CsvBindAndJoinByPosition.class);
required = annotation.required();
fieldLocale = annotation.locale();
Class<?> elementType = annotation.elementType();
Class<? extends MultiValuedMap> mapType = annotation.mapType();
Class<? extends AbstractCsvConverter> joinConverter = annotation.converter();
capture = annotation.capture();
format = annotation.format();
CsvConverter converter = determineConverter(field, elementType, fieldLocale, joinConverter);
fieldMap.putComplex(annotation.position(), new BeanFieldJoinIntegerIndex<T>(
field, required, errorLocale, converter, mapType, capture, format));
}
// Then it must be a bind by position.
else {
CsvBindByPosition annotation = field.getAnnotation(CsvBindByPosition.class);
required = annotation.required();
fieldLocale = annotation.locale();
capture = annotation.capture();
format = annotation.format();
CsvConverter converter = determineConverter(field, field.getType(), fieldLocale, null);
fieldMap.put(annotation.position(), new BeanFieldSingleValue<T>(
field, required, errorLocale, converter, capture, format));
}
}
}
#Override
public void verifyLineLength(int numberOfFields) throws CsvRequiredFieldEmptyException {
if (!headerIndex.isEmpty()) {
BeanField f;
StringBuilder sb = null;
for (int i = numberOfFields; i <= headerIndex.findMaxIndex(); i++) {
f = findField(i);
if (f != null && f.isRequired()) {
if (sb == null) {
sb = new StringBuilder(ResourceBundle.getBundle(ICSVParser.DEFAULT_BUNDLE_NAME, errorLocale).getString("multiple.required.field.empty"));
}
sb.append(' ');
sb.append(f.getField().getName());
}
}
if (sb != null) {
throw new CsvRequiredFieldEmptyException(type, sb.toString());
}
}
}
private List<Field> loadFields(Class<? extends T> cls) {
List<Field> fields = new LinkedList<>();
for (Field field : FieldUtils.getAllFields(cls)) {
if (field.isAnnotationPresent(CsvBindByPosition.class)
|| field.isAnnotationPresent(CsvCustomBindByPosition.class)
|| field.isAnnotationPresent(CsvBindAndJoinByPosition.class)
|| field.isAnnotationPresent(CsvBindAndSplitByPosition.class)) {
fields.add(field);
}
}
setAnnotationDriven(!fields.isEmpty());
return fields;
}
/**
* Returns the column position for the given column number.
* Yes, they're the same thing. For this mapping strategy, it's a simple
* conversion from an integer to a string.
*/
// The rest of the Javadoc is inherited
#Override
public String findHeader(int col) {
return Integer.toString(col);
}
#Override
protected FieldMap<String, Integer, ? extends ComplexFieldMapEntry<String, Integer, T>, T> getFieldMap() {
return fieldMap;
}
/**
* Sets the {#link java.util.Comparator} to be used to sort columns when
* writing beans to a CSV file.
* Behavior of this method when used on a mapping strategy intended for
* reading data from a CSV source is not defined.
*
* #param writeOrder The {#link java.util.Comparator} to use. May be
* {#code null}, in which case the natural ordering is used.
* #since 4.3
*/
public void setColumnOrderOnWrite(Comparator<Integer> writeOrder) {
this.writeOrder = writeOrder;
if (fieldMap != null) {
fieldMap.setColumnOrderOnWrite(this.writeOrder);
}
}
}
Have you tried
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.generateHeader(bean);
return HEADER;
}
I was trying to construct an object of the MTree class (https://github.com/Waikato/moa/blob/master/moa/src/main/java/moa/clusterers/outliers/utils/mtree/MTree.java)
The constructor of MTree looks like this:
public MTree(DistanceFunction<? super DATA> distanceFunction,
SplitFunction<DATA> splitFunction) {
this(DEFAULT_MIN_NODE_CAPACITY, distanceFunction, splitFunction);
}
The DistanceFunction here is an interface, the code of it is:
/**
* An object that can calculate the distance between two data objects.
*
* #param <DATA> The type of the data objects.
*/
public interface DistanceFunction<DATA> {
double calculate(DATA data1, DATA data2);
}
And it's implementation is:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Some pre-defined implementations of {#linkplain DistanceFunction distance
* functions}.
*/
public final class DistanceFunctions {
/**
* Don't let anyone instantiate this class.
*/
private DistanceFunctions() {}
/**
* Creates a cached version of a {#linkplain DistanceFunction distance
* function}. This method is used internally by {#link MTree} to create
* a cached distance function to pass to the {#linkplain SplitFunction split
* function}.
* #param distanceFunction The distance function to create a cached version
* of.
* #return The cached distance function.
*/
public static <Data> DistanceFunction<Data> cached(final DistanceFunction<Data> distanceFunction) {
return new DistanceFunction<Data>() {
class Pair {
Data data1;
Data data2;
public Pair(Data data1, Data data2) {
this.data1 = data1;
this.data2 = data2;
}
#Override
public int hashCode() {
return data1.hashCode() ^ data2.hashCode();
}
#Override
public boolean equals(Object arg0) {
if(arg0 instanceof Pair) {
Pair that = (Pair) arg0;
return this.data1.equals(that.data1)
&& this.data2.equals(that.data2);
} else {
return false;
}
}
}
private final Map<Pair, Double> cache = new HashMap<Pair, Double>();
#Override
public double calculate(Data data1, Data data2) {
Pair pair1 = new Pair(data1, data2);
Double distance = cache.get(pair1);
if(distance != null) {
return distance;
}
Pair pair2 = new Pair(data2, data1);
distance = cache.get(pair2);
if(distance != null) {
return distance;
}
distance = distanceFunction.calculate(data1, data2);
cache.put(pair1, distance);
cache.put(pair2, distance);
return distance;
}
};
}
/**
* An interface to represent coordinates in Euclidean spaces.
* #see <a href="http://en.wikipedia.org/wiki/Euclidean_space">"Euclidean
* Space" article at Wikipedia</a>
*/
public interface EuclideanCoordinate {
/**
* The number of dimensions.
*/
int dimensions();
/**
* A method to access the {#code index}-th component of the coordinate.
*
* #param index The index of the component. Must be less than {#link
* #dimensions()}.
*/
double get(int index);
}
/**
* Calculates the distance between two {#linkplain EuclideanCoordinate
* euclidean coordinates}.
*/
public static double euclidean(EuclideanCoordinate coord1, EuclideanCoordinate coord2) {
int size = Math.min(coord1.dimensions(), coord2.dimensions());
double distance = 0;
for(int i = 0; i < size; i++) {
double diff = coord1.get(i) - coord2.get(i);
distance += diff * diff;
}
distance = Math.sqrt(distance);
return distance;
}
/**
* A {#linkplain DistanceFunction distance function} object that calculates
* the distance between two {#linkplain EuclideanCoordinate euclidean
* coordinates}.
*/
public static final DistanceFunction<EuclideanCoordinate> EUCLIDEAN = new DistanceFunction<DistanceFunctions.EuclideanCoordinate>() {
#Override
public double calculate(EuclideanCoordinate coord1, EuclideanCoordinate coord2) {
return DistanceFunctions.euclidean(coord1, coord2);
}
};
/**
* A {#linkplain DistanceFunction distance function} object that calculates
* the distance between two coordinates represented by {#linkplain
* java.util.List lists} of {#link java.lang.Integer}s.
*/
public static final DistanceFunction<List<Integer>> EUCLIDEAN_INTEGER_LIST = new DistanceFunction<List<Integer>>() {
#Override
public double calculate(List<Integer> data1, List<Integer> data2) {
class IntegerListEuclideanCoordinate implements EuclideanCoordinate {
List<Integer> list;
public IntegerListEuclideanCoordinate(List<Integer> list) { this.list = list; }
#Override public int dimensions() { return list.size(); }
#Override public double get(int index) { return list.get(index); }
};
IntegerListEuclideanCoordinate coord1 = new IntegerListEuclideanCoordinate(data1);
IntegerListEuclideanCoordinate coord2 = new IntegerListEuclideanCoordinate(data2);
return DistanceFunctions.euclidean(coord1, coord2);
}
};
/**
* A {#linkplain DistanceFunction distance function} object that calculates
* the distance between two coordinates represented by {#linkplain
* java.util.List lists} of {#link java.lang.Double}s.
*/
public static final DistanceFunction<List<Double>> EUCLIDEAN_DOUBLE_LIST = new DistanceFunction<List<Double>>() {
#Override
public double calculate(List<Double> data1, List<Double> data2) {
class DoubleListEuclideanCoordinate implements EuclideanCoordinate {
List<Double> list;
public DoubleListEuclideanCoordinate(List<Double> list) { this.list = list; }
#Override public int dimensions() { return list.size(); }
#Override public double get(int index) { return list.get(index); }
};
DoubleListEuclideanCoordinate coord1 = new DoubleListEuclideanCoordinate(data1);
DoubleListEuclideanCoordinate coord2 = new DoubleListEuclideanCoordinate(data2);
return DistanceFunctions.euclidean(coord1, coord2);
}
};
}
And my first question is what's the meaning of return new DistanceFunction<Data>() in the method public static <Data> DistanceFunction<Data> cached(final DistanceFunction<Data> distanceFunction) [the method is in the class DistanceFunctions] I am just a beginner of Java and this one is a little bit hard for me to understand.
Also, to create an object of MTree, I should create an object of DistanceFunctions and an object of ComposedSplitFunction(Which is the implementation of SplitFunction interface) and input them as parameter for MTree constructor. But I really don't know how to do that because in DistanceFunctions class, the constructor is private. So I cannot generate a parameter for the constructor of MTree.
What should I do?
New Update: What I want to do is create a Junit Test for MTree, and I believe the first thing I need to do is create an object of MTree.
Interfaces can have multiple implementations. They just form the general contract implementations need to follow.
The cache implementation here i.e. takes a DistanceFunction as input and guarantees that distance values between A and B (or B and A) are only calculated once and thereafter served from the internal cache map. The generic type of that cache function just guarantees that you can literally pass any type to it. I.e. you could have an implementation that takes in its simplest form just two integers and calculates the difference of these like this:
DistanceFunction<Integer> func = (Integer a, Integer b) -> Math.abs(a - b);
which is a labmda expression which could be also written a bit more verbose like this
DistanceFunction<Integer> func = new DistanceFunction<Integer>() {
#Override
public double calculate(Integer data1, Integer data2) {
return Math.abs(data1 - data2);
}
};
and then use it like that to cache the return value for the provided input parameters:
DistanceFunction<Integer> cache = DistanceFunctions.cached(func);
double distance = cache.calculate(10, 5);
If you later on have a call like
distance = cache.calculate(10, 5);
again or even
distance = cache.calculate(5, 10);
The distance value in the above case is not recalculated but its value is returned from the internal cache map as the distance for these parameters was already calculated before. This is especially benefitial if you have plenty of data points but only a limited number of combination of these and calculation is rather expensive.
If you further look into the DistanceFunctions class you've provided you will see that it already provides some implementations for i.e. EUCLIDEAN, EUCLIDEAN_INTEGER_LIST and EUCLIDEAN_DOUBLE_LIST implementations which due to their static final nature can be used as constant in your code directly. Here you just need to provide matching input arguments to the calculate(...) method based on the implementation you've chosen.
In regards to Waikato's MTree` initialization, a rough template may look like this:
MTree mTree = new MTree(EUCLIDEAN_INTEGER_LIST, new SplitFunction<List<Integer>>(...) {
...
#Override
public SplitResult<List<Integer>> process(Set<List<Integer>> dataSet, DistanceFunction<? super List<Integer>> distanceFunction) {
Pair<List<Integer>> promoted = ...
Pair<Set<List<Integer>>> partitions = ...
return new SplitResult<List<Integer>>(promoted, partitions);
}
});
where the parts outlined by ... need to be defined and implemented by you. The code in that package though provides i.e. a ComposedSplitFunction implementation already that requires PartitionFunction and PromotionFunction as inputs where implementations of these are already available in the PartitionFunctions and PromotionFunctions classes that just work the same way as the DistanceFunction and DistanceFunctions discussed here.
I want to take an existing enum and add more elements to it as follows:
enum A {a,b,c}
enum B extends A {d}
/*B is {a,b,c,d}*/
Is this possible in Java?
No, you can't do this in Java. Aside from anything else, d would then presumably be an instance of A (given the normal idea of "extends"), but users who only knew about A wouldn't know about it - which defeats the point of an enum being a well-known set of values.
If you could tell us more about how you want to use this, we could potentially suggest alternative solutions.
Enums represent a complete enumeration of possible values. So the (unhelpful) answer is no.
As an example of a real problem take weekdays, weekend days and, the union, days of week. We could define all days within days-of-week but then we would not be able to represent properties special to either weekdays and weekend-days.
What we could do, is have three enum types with a mapping between weekdays/weekend-days and days-of-week.
public enum Weekday {
MON, TUE, WED, THU, FRI;
public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay {
SAT, SUN;
public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
Alternatively, we could have an open-ended interface for day-of-week:
interface Day {
...
}
public enum Weekday implements Day {
MON, TUE, WED, THU, FRI;
}
public enum WeekendDay implements Day {
SAT, SUN;
}
Or we could combine the two approaches:
interface Day {
...
}
public enum Weekday implements Day {
MON, TUE, WED, THU, FRI;
public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay implements Day {
SAT, SUN;
public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN;
public Day toDay() { ... }
}
The recommended solution to this is the extensible enum pattern.
This involves creating an interface and using that where you currently use the enum. Then make the enum implement the interface. You can add more constants by adding an additional enum/class that also extends the interface. Here is the general idea:
public interface TrafficLights {
public abstract String getColour();
}
public enum StandardTrafficLights implements TrafficLights {
RED, YELLOW, GREEN;
public String getColour() {
return name();
}
}
public enum WeirdTrafficLights implements TrafficLights {
DOUBLE_RED;
public String getColour() {
return name();
}
}
Note that if you want something like TrafficLights.valueof(String) you will have to implement it yourself.
Under the covers your ENUM is just a regular class generated by the compiler. That generated class extends java.lang.Enum. The technical reason you can't extend the generated class is that the generated class is final. The conceptual reasons for it being final are discussed in this topic. But I'll add the mechanics to the discussion.
Here is a test enum:
public enum TEST {
ONE, TWO, THREE;
}
The resulting code from javap:
public final class TEST extends java.lang.Enum<TEST> {
public static final TEST ONE;
public static final TEST TWO;
public static final TEST THREE;
static {};
public static TEST[] values();
public static TEST valueOf(java.lang.String);
}
Conceivably you could type this class on your own and drop the "final". But the compiler prevents you from extending "java.lang.Enum" directly. You could decide NOT to extend java.lang.Enum, but then your class and its derived classes would not be an instanceof java.lang.Enum ... which might not really matter to you any way!
enum A {a,b,c}
enum B extends A {d}
/*B is {a,b,c,d}*/
can be written as:
public enum All {
a (ClassGroup.A,ClassGroup.B),
b (ClassGroup.A,ClassGroup.B),
c (ClassGroup.A,ClassGroup.B),
d (ClassGroup.B)
...
ClassGroup.B.getMembers() contains {a,b,c,d}
How it can be useful: Let say we want something like:
We have events and we are using enums. Those enums can be grouped by similar processing. If we have operation with many elements, then some events starts operation, some are just step and other end the operation. To gather such operation and avoid long switch case we can group them as in example and use:
if(myEvent.is(State_StatusGroup.START)) makeNewOperationObject()..
if(myEnum.is(State_StatusGroup.STEP)) makeSomeSeriousChanges()..
if(myEnum.is(State_StatusGroup.FINISH)) closeTransactionOrSomething()..
Example:
public enum AtmOperationStatus {
STARTED_BY_SERVER (State_StatusGroup.START),
SUCCESS (State_StatusGroup.FINISH),
FAIL_TOKEN_TIMEOUT (State_StatusGroup.FAIL,
State_StatusGroup.FINISH),
FAIL_NOT_COMPLETE (State_StatusGroup.FAIL,
State_StatusGroup.STEP),
FAIL_UNKNOWN (State_StatusGroup.FAIL,
State_StatusGroup.FINISH),
(...)
private AtmOperationStatus(StatusGroupInterface ... pList){
for (StatusGroupInterface group : pList){
group.addMember(this);
}
}
public boolean is(StatusGroupInterface with){
for (AtmOperationStatus eT : with.getMembers()){
if( eT .equals(this)) return true;
}
return false;
}
// Each group must implement this interface
private interface StatusGroupInterface{
EnumSet<AtmOperationStatus> getMembers();
void addMember(AtmOperationStatus pE);
}
// DEFINING GROUPS
public enum State_StatusGroup implements StatusGroupInterface{
START, STEP, FAIL, FINISH;
private List<AtmOperationStatus> members = new LinkedList<AtmOperationStatus>();
#Override
public EnumSet<AtmOperationStatus> getMembers() {
return EnumSet.copyOf(members);
}
#Override
public void addMember(AtmOperationStatus pE) {
members.add(pE);
}
static { // forcing initiation of dependent enum
try {
Class.forName(AtmOperationStatus.class.getName());
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Class AtmEventType not found", ex);
}
}
}
}
//Some use of upper code:
if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.FINISH)) {
//do something
}else if (p.getStatus().is(AtmOperationStatus.State_StatusGroup.START)) {
//do something
}
Add some more advanced:
public enum AtmEventType {
USER_DEPOSIT (Status_EventsGroup.WITH_STATUS,
Authorization_EventsGroup.USER_AUTHORIZED,
ChangedMoneyAccountState_EventsGroup.CHANGED,
OperationType_EventsGroup.DEPOSIT,
ApplyTo_EventsGroup.CHANNEL),
SERVICE_DEPOSIT (Status_EventsGroup.WITH_STATUS,
Authorization_EventsGroup.TERMINAL_AUTHORIZATION,
ChangedMoneyAccountState_EventsGroup.CHANGED,
OperationType_EventsGroup.DEPOSIT,
ApplyTo_EventsGroup.CHANNEL),
DEVICE_MALFUNCTION (Status_EventsGroup.WITHOUT_STATUS,
Authorization_EventsGroup.TERMINAL_AUTHORIZATION,
ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED,
ApplyTo_EventsGroup.DEVICE),
CONFIGURATION_4_C_CHANGED(Status_EventsGroup.WITHOUT_STATUS,
ApplyTo_EventsGroup.TERMINAL,
ChangedMoneyAccountState_EventsGroup.DID_NOT_CHANGED),
(...)
At above if we have some fail (myEvent.is(State_StatusGroup.FAIL)) then iterating by previous events we can easily check if we must revert money transfer by:
if(myEvent2.is(ChangedMoneyAccountState_EventsGroup.CHANGED)) rollBack()..
It can be useful for:
including explicite meta-data about processing logic, less to remember
implementing some of multi-inheritance
we don't want to use class structures, ex. for sending short status messages
In case you missed it, there's a chapter in the excellent Joshua Bloch's book "Effective Java, 2nd edition".
Chapter 6 - Enums and Annotations
Item 34 : Emulate extensible enums with interfaces
Just the conclusion :
A minor disadvantage of the use of interfaces to emulate extensible enums is
those implementations cannot be inherited from one enum type to another. In the
case of our Operation example, the logic to store and retrieve the symbol associated
with an operation is duplicated in BasicOperation and ExtendedOperation.
In this case, it doesn’t matter because very little code is duplicated. If there were a
a larger amount of shared functionality, you could encapsulate it in a helper class or
a static helper method to eliminate the code duplication.
In summary, while you cannot write an extensible enum type, you can
emulate it by writing an interface to go with a basic enum type that implements
the interface. This allows clients to write their own enums that implement
the interface. These enums can then be used wherever the basic enum type can be
used, assuming APIs are written in terms of the interface.
Here is a way how I found how to extend a enum into other enum, is a very straighfoward approach:
Suposse you have a enum with common constants:
public interface ICommonInterface {
String getName();
}
public enum CommonEnum implements ICommonInterface {
P_EDITABLE("editable"),
P_ACTIVE("active"),
P_ID("id");
private final String name;
EnumCriteriaComun(String name) {
name= name;
}
#Override
public String getName() {
return this.name;
}
}
then you can try to do a manual extends in this way:
public enum SubEnum implements ICommonInterface {
P_EDITABLE(CommonEnum.P_EDITABLE ),
P_ACTIVE(CommonEnum.P_ACTIVE),
P_ID(CommonEnum.P_ID),
P_NEW_CONSTANT("new_constant");
private final String name;
EnumCriteriaComun(CommonEnum commonEnum) {
name= commonEnum.name;
}
EnumCriteriaComun(String name) {
name= name;
}
#Override
public String getName() {
return this.name;
}
}
of course every time you need to extend a constant you have to modify your SubEnum files.
This is how I enhance the enum inheritance pattern with runtime check in static initializer.
The BaseKind#checkEnumExtender checks that "extending" enum declares all the values of the base enum in exactly the same way so #name() and #ordinal() remain fully compatible.
There is still copy-paste involved for declaring values but the program fails fast if somebody added or modified a value in the base class without updating extending ones.
Common behavior for different enums extending each other:
public interface Kind {
/**
* Let's say we want some additional member.
*/
String description() ;
/**
* Standard {#code Enum} method.
*/
String name() ;
/**
* Standard {#code Enum} method.
*/
int ordinal() ;
}
Base enum, with verifying method:
public enum BaseKind implements Kind {
FIRST( "First" ),
SECOND( "Second" ),
;
private final String description ;
public String description() {
return description ;
}
private BaseKind( final String description ) {
this.description = description ;
}
public static void checkEnumExtender(
final Kind[] baseValues,
final Kind[] extendingValues
) {
if( extendingValues.length < baseValues.length ) {
throw new IncorrectExtensionError( "Only " + extendingValues.length + " values against "
+ baseValues.length + " base values" ) ;
}
for( int i = 0 ; i < baseValues.length ; i ++ ) {
final Kind baseValue = baseValues[ i ] ;
final Kind extendingValue = extendingValues[ i ] ;
if( baseValue.ordinal() != extendingValue.ordinal() ) {
throw new IncorrectExtensionError( "Base ordinal " + baseValue.ordinal()
+ " doesn't match with " + extendingValue.ordinal() ) ;
}
if( ! baseValue.name().equals( extendingValue.name() ) ) {
throw new IncorrectExtensionError( "Base name[ " + i + "] " + baseValue.name()
+ " doesn't match with " + extendingValue.name() ) ;
}
if( ! baseValue.description().equals( extendingValue.description() ) ) {
throw new IncorrectExtensionError( "Description[ " + i + "] " + baseValue.description()
+ " doesn't match with " + extendingValue.description() ) ;
}
}
}
public static class IncorrectExtensionError extends Error {
public IncorrectExtensionError( final String s ) {
super( s ) ;
}
}
}
Extension sample:
public enum ExtendingKind implements Kind {
FIRST( BaseKind.FIRST ),
SECOND( BaseKind.SECOND ),
THIRD( "Third" ),
;
private final String description ;
public String description() {
return description ;
}
ExtendingKind( final BaseKind baseKind ) {
this.description = baseKind.description() ;
}
ExtendingKind( final String description ) {
this.description = description ;
}
}
Based on #Tom Hawtin - tackline answer we add switch support,
interface Day<T> {
...
T valueOf();
}
public enum Weekday implements Day<Weekday> {
MON, TUE, WED, THU, FRI;
Weekday valueOf(){
return valueOf(name());
}
}
public enum WeekendDay implements Day<WeekendDay> {
SAT, SUN;
WeekendDay valueOf(){
return valueOf(name());
}
}
Day<Weekday> wds = Weekday.MON;
Day<WeekendDay> wends = WeekendDay.SUN;
switch(wds.valueOf()){
case MON:
case TUE:
case WED:
case THU:
case FRI:
}
switch(wends.valueOf()){
case SAT:
case SUN:
}
I tend to avoid enums, because they are not extensible. To stay with the example of the OP, if A is in a library and B in your own code, you can't extend A if it is an enum. This is how I sometimes replace enums:
// access like enum: A.a
public class A {
public static final A a = new A();
public static final A b = new A();
public static final A c = new A();
/*
* In case you need to identify your constant
* in different JVMs, you need an id. This is the case if
* your object is transfered between
* different JVM instances (eg. save/load, or network).
* Also, switch statements don't work with
* Objects, but work with int.
*/
public static int maxId=0;
public int id = maxId++;
public int getId() { return id; }
}
public class B extends A {
/*
* good: you can do like
* A x = getYourEnumFromSomeWhere();
* if(x instanceof B) ...;
* to identify which enum x
* is of.
*/
public static final A d = new A();
}
public class C extends A {
/* Good: e.getId() != d.getId()
* Bad: in different JVMs, C and B
* might be initialized in different order,
* resulting in different IDs.
* Workaround: use a fixed int, or hash code.
*/
public static final A e = new A();
public int getId() { return -32489132; };
}
There are some pits to avoid, see the comments in the code. Depending on your needs, this is a solid, extensible alternative to enums.
I suggest you take the other way around approach.
Instead of extending the existing enumeration, create a larger one and create a subset of it.
For exemple if you had an enumeration called PET and you wanted to extend it to ANIMAL you should do this instead:
public enum ANIMAL {
WOLF,CAT, DOG
}
EnumSet<ANIMAL> pets = EnumSet.of(ANIMAL.CAT, ANIMAL.DOG);
Be careful, pets is not an immutable collections, you might want to use Guava or Java9 for more safety.
As an aid to understanding why extending an Enum is not reasonable at the language implementation level to consider what would happen if you passed an instance of the extended Enum to a routine that only understands the base Enum. A switch that the compiler promised had all cases covered would in fact not cover those extended Enum values.
This further emphasizes that Java Enum values are not integers such as C's are, for instances: to use a Java Enum as an array index you must explicitly ask for its ordinal() member, to give a java Enum an arbitrary integer value you must add an explicit field for that and reference that named member.
This is not a comment on the OP's desire, just on why Java ain't never going to do it.
Having had this same problem myself I'd like to post my perspective. I think that there are a couple motivating factors for doing something like this:
You want to have some related enum codes, but in different classes. In my case I had a base class with several codes defined in an associated enum. At some later date (today!) I wanted to provide some new functionality to the base class, which also meant new codes for the enum.
The derived class would support both the base classes' enum as well as its own. No duplicate enum values! So: how to have an enum for the subclass that includes the enum's of its parent along with its new values.
Using an interface doesn't really cut it: you can accidentally get duplicate enum values. Not desirable.
I ended up just combining the enums: this ensures that there cannot be any duplicate values, at the expense of being less tightly tied to its associated class. But, I figured the duplicate issue was my main concern...
In the hopes this elegant solution of a colleague of mine is even seen in this long post I'd like to share this approach for subclassing which follows the interface approach and beyond.
Please be aware that we use custom exceptions here and this code won't compile unless you replace it with your exceptions.
The documentation is extensive and I hope it's understandable for most of you.
The interface that every subclassed enum needs to implement.
public interface Parameter {
/**
* Retrieve the parameters name.
*
* #return the name of the parameter
*/
String getName();
/**
* Retrieve the parameters type.
*
* #return the {#link Class} according to the type of the parameter
*/
Class<?> getType();
/**
* Matches the given string with this parameters value pattern (if applicable). This helps to find
* out if the given string is a syntactically valid candidate for this parameters value.
*
* #param valueStr <i>optional</i> - the string to check for
* #return <code>true</code> in case this parameter has no pattern defined or the given string
* matches the defined one, <code>false</code> in case <code>valueStr</code> is
* <code>null</code> or an existing pattern is not matched
*/
boolean match(final String valueStr);
/**
* This method works as {#link #match(String)} but throws an exception if not matched.
*
* #param valueStr <i>optional</i> - the string to check for
* #throws ArgumentException with code
* <dl>
* <dt>PARAM_MISSED</dt>
* <dd>if <code>valueStr</code> is <code>null</code></dd>
* <dt>PARAM_BAD</dt>
* <dd>if pattern is not matched</dd>
* </dl>
*/
void matchEx(final String valueStr) throws ArgumentException;
/**
* Parses a value for this parameter from the given string. This method honors the parameters data
* type and potentially other criteria defining a valid value (e.g. a pattern).
*
* #param valueStr <i>optional</i> - the string to parse the parameter value from
* #return the parameter value according to the parameters type (see {#link #getType()}) or
* <code>null</code> in case <code>valueStr</code> was <code>null</code>.
* #throws ArgumentException in case <code>valueStr</code> is not parsable as a value for this
* parameter.
*/
Object parse(final String valueStr) throws ArgumentException;
/**
* Converts the given value to its external form as it is accepted by {#link #parse(String)}. For
* most (ordinary) parameters this is simply a call to {#link String#valueOf(Object)}. In case the
* parameter types {#link Object#toString()} method does not return the external form (e.g. for
* enumerations), this method has to be implemented accordingly.
*
* #param value <i>mandatory</i> - the parameters value
* #return the external form of the parameters value, never <code>null</code>
* #throws InternalServiceException in case the given <code>value</code> does not match
* {#link #getType()}
*/
String toString(final Object value) throws InternalServiceException;
}
The implementing ENUM base class.
public enum Parameters implements Parameter {
/**
* ANY ENUM VALUE
*/
VALUE(new ParameterImpl<String>("VALUE", String.class, "[A-Za-z]{3,10}"));
/**
* The parameter wrapped by this enum constant.
*/
private Parameter param;
/**
* Constructor.
*
* #param param <i>mandatory</i> - the value for {#link #param}
*/
private Parameters(final Parameter param) {
this.param = param;
}
/**
* {#inheritDoc}
*/
#Override
public String getName() {
return this.param.getName();
}
/**
* {#inheritDoc}
*/
#Override
public Class<?> getType() {
return this.param.getType();
}
/**
* {#inheritDoc}
*/
#Override
public boolean match(final String valueStr) {
return this.param.match(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public void matchEx(final String valueStr) {
this.param.matchEx(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public Object parse(final String valueStr) throws ArgumentException {
return this.param.parse(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public String toString(final Object value) throws InternalServiceException {
return this.param.toString(value);
}
}
The subclassed ENUM which "inherits" from base class.
public enum ExtendedParameters implements Parameter {
/**
* ANY ENUM VALUE
*/
VALUE(my.package.name.VALUE);
/**
* EXTENDED ENUM VALUE
*/
EXTENDED_VALUE(new ParameterImpl<String>("EXTENDED_VALUE", String.class, "[0-9A-Za-z_.-]{1,20}"));
/**
* The parameter wrapped by this enum constant.
*/
private Parameter param;
/**
* Constructor.
*
* #param param <i>mandatory</i> - the value for {#link #param}
*/
private Parameters(final Parameter param) {
this.param = param;
}
/**
* {#inheritDoc}
*/
#Override
public String getName() {
return this.param.getName();
}
/**
* {#inheritDoc}
*/
#Override
public Class<?> getType() {
return this.param.getType();
}
/**
* {#inheritDoc}
*/
#Override
public boolean match(final String valueStr) {
return this.param.match(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public void matchEx(final String valueStr) {
this.param.matchEx(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public Object parse(final String valueStr) throws ArgumentException {
return this.param.parse(valueStr);
}
/**
* {#inheritDoc}
*/
#Override
public String toString(final Object value) throws InternalServiceException {
return this.param.toString(value);
}
}
Finally the generic ParameterImpl to add some utilities.
public class ParameterImpl<T> implements Parameter {
/**
* The default pattern for numeric (integer, long) parameters.
*/
private static final Pattern NUMBER_PATTERN = Pattern.compile("[0-9]+");
/**
* The default pattern for parameters of type boolean.
*/
private static final Pattern BOOLEAN_PATTERN = Pattern.compile("0|1|true|false");
/**
* The name of the parameter, never <code>null</code>.
*/
private final String name;
/**
* The data type of the parameter.
*/
private final Class<T> type;
/**
* The validation pattern for the parameters values. This may be <code>null</code>.
*/
private final Pattern validator;
/**
* Shortcut constructor without <code>validatorPattern</code>.
*
* #param name <i>mandatory</i> - the value for {#link #name}
* #param type <i>mandatory</i> - the value for {#link #type}
*/
public ParameterImpl(final String name, final Class<T> type) {
this(name, type, null);
}
/**
* Constructor.
*
* #param name <i>mandatory</i> - the value for {#link #name}
* #param type <i>mandatory</i> - the value for {#link #type}
* #param validatorPattern - <i>optional</i> - the pattern for {#link #validator}
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>The default validation patterns {#link #NUMBER_PATTERN} or
* {#link #BOOLEAN_PATTERN} are applied accordingly.
* </dl>
*/
public ParameterImpl(final String name, final Class<T> type, final String validatorPattern) {
this.name = name;
this.type = type;
if (null != validatorPattern) {
this.validator = Pattern.compile(validatorPattern);
} else if (Integer.class == this.type || Long.class == this.type) {
this.validator = NUMBER_PATTERN;
} else if (Boolean.class == this.type) {
this.validator = BOOLEAN_PATTERN;
} else {
this.validator = null;
}
}
/**
* {#inheritDoc}
*/
#Override
public boolean match(final String valueStr) {
if (null == valueStr) {
return false;
}
if (null != this.validator) {
final Matcher matcher = this.validator.matcher(valueStr);
return matcher.matches();
}
return true;
}
/**
* {#inheritDoc}
*/
#Override
public void matchEx(final String valueStr) throws ArgumentException {
if (false == this.match(valueStr)) {
if (null == valueStr) {
throw ArgumentException.createEx(ErrorCode.PARAM_MISSED, "The value must not be null",
this.name);
}
throw ArgumentException.createEx(ErrorCode.PARAM_BAD, "The value must match the pattern: "
+ this.validator.pattern(), this.name);
}
}
/**
* Parse the parameters value from the given string value according to {#link #type}. Additional
* the value is checked by {#link #matchEx(String)}.
*
* #param valueStr <i>optional</i> - the string value to parse the value from
* #return the parsed value, may be <code>null</code>
* #throws ArgumentException in case the parameter:
* <ul>
* <li>does not {#link #matchEx(String)} the {#link #validator}</li>
* <li>cannot be parsed according to {#link #type}</li>
* </ul>
* #throws InternalServiceException in case the type {#link #type} cannot be handled. This is a
* programming error.
*/
#Override
public T parse(final String valueStr) throws ArgumentException, InternalServiceException {
if (null == valueStr) {
return null;
}
this.matchEx(valueStr);
if (String.class == this.type) {
return this.type.cast(valueStr);
}
if (Boolean.class == this.type) {
return this.type.cast(Boolean.valueOf(("1".equals(valueStr)) || Boolean.valueOf(valueStr)));
}
try {
if (Integer.class == this.type) {
return this.type.cast(Integer.valueOf(valueStr));
}
if (Long.class == this.type) {
return this.type.cast(Long.valueOf(valueStr));
}
} catch (final NumberFormatException e) {
throw ArgumentException.createEx(ErrorCode.PARAM_BAD, "The value cannot be parsed as "
+ this.type.getSimpleName().toLowerCase() + ".", this.name);
}
return this.parseOther(valueStr);
}
/**
* Field access for {#link #name}.
*
* #return the value of {#link #name}.
*/
#Override
public String getName() {
return this.name;
}
/**
* Field access for {#link #type}.
*
* #return the value of {#link #type}.
*/
#Override
public Class<T> getType() {
return this.type;
}
/**
* {#inheritDoc}
*/
#Override
public final String toString(final Object value) throws InternalServiceException {
if (false == this.type.isAssignableFrom(value.getClass())) {
throw new InternalServiceException(ErrorCode.PANIC,
"Parameter.toString(): Bad type of value. Expected {0} but is {1}.", this.type.getName(),
value.getClass().getName());
}
if (String.class == this.type || Integer.class == this.type || Long.class == this.type) {
return String.valueOf(value);
}
if (Boolean.class == this.type) {
return Boolean.TRUE.equals(value) ? "1" : "0";
}
return this.toStringOther(value);
}
/**
* Parse parameter values of other (non standard types). This method is called by
* {#link #parse(String)} in case {#link #type} is none of the supported standard types (currently
* String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* #param valueStr <i>mandatory</i> - the string value to parse the value from
* #return the parsed value, may be <code>null</code>
* #throws ArgumentException in case the parameter cannot be parsed according to {#link #type}
* #throws InternalServiceException in case the type {#link #type} cannot be handled. This is a
* programming error.
*/
protected T parseOther(final String valueStr) throws ArgumentException, InternalServiceException {
throw new InternalServiceException(ErrorCode.PANIC,
"ParameterImpl.parseOther(): Unsupported parameter type: " + this.type.getName());
}
/**
* Convert the values of other (non standard types) to their external form. This method is called
* by {#link #toString(Object)} in case {#link #type} is none of the supported standard types
* (currently String, Boolean, Integer and Long). It is intended for extensions.
* <dl>
* <dt style="margin-top:0.25cm;"><i>Note:</i>
* <dd>This default implementation always throws an InternalServiceException.
* </dl>
*
* #param value <i>mandatory</i> - the parameters value
* #return the external form of the parameters value, never <code>null</code>
* #throws InternalServiceException in case the given <code>value</code> does not match
* {#link #getClass()}
*/
protected String toStringOther(final Object value) throws InternalServiceException {
throw new InternalServiceException(ErrorCode.PANIC,
"ParameterImpl.toStringOther(): Unsupported parameter type: " + this.type.getName());
}
}
My way to code that would be as follows:
// enum A { a, b, c }
static final Set<Short> enumA = new LinkedHashSet<>(Arrays.asList(new Short[]{'a','b','c'}));
// enum B extends A { d }
static final Set<Short> enumB = new LinkedHashSet<>(enumA);
static {
enumB.add((short) 'd');
// If you have to add more elements:
// enumB.addAll(Arrays.asList(new Short[]{ 'e', 'f', 'g', '♯', '♭' }));
}
LinkedHashSet provides both that each entry only exists once, and that their order is preserved. If order doesn’t matter, you can use HashSet instead. The following code is not possible in Java:
for (A a : B.values()) { // enum B extends A { d }
switch (a) {
case a:
case b:
case c:
System.out.println("Value is: " + a.toString());
break;
default:
throw new IllegalStateException("This should never happen.");
}
}
The code can be written as follows:
for (Short a : enumB) {
switch (a) {
case 'a':
case 'b':
case 'c':
System.out.println("Value is: " + new String(Character.toChars(a)));
break;
default:
throw new IllegalStateException("This should never happen.");
}
}
From Java 7 onwards you can even do the same with String:
// enum A { BACKWARDS, FOREWARDS, STANDING }
static final Set<String> enumA = new LinkedHashSet<>(Arrays.asList(new String[] {
"BACKWARDS", "FOREWARDS", "STANDING" }));
// enum B extends A { JUMP }
static final Set<String> enumB = new LinkedHashSet<>(enumA);
static {
enumB.add("JUMP");
}
Using the enum replacement:
for (String a : enumB) {
switch (a) {
case "BACKWARDS":
case "FOREWARDS":
case "STANDING":
System.out.println("Value is: " + a);
break;
default:
throw new IllegalStateException("This should never happen.");
}
}
I am starting to develop my skills in JAVA, however I have a doubt.
I'm creating an object in JAVA, created the constructor and so on, then, it asks "Change the AGE_RECENT value from 1 to 3", I initially declared this as final because I never thought it would change, so no SET or GET were created. I am wondering how can I change the value from 1 to 3 in the SET Method.
I have this variable
private static int AGE_RECENT=1;
I did this.
public void setAgeRecent() {
Vehicle.AGE_RECENT = 3;
}
It works if you run the program, it changes the variable's value, however nothing was declared in that method as every SET method.
Just wondering how can I do this. If this is correct, good, if not, thanks for helping!
As someone asked, the code.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tp1;
/**
*
* #author Nelson
*/
public class Vehicle {
/** Variáveis da classe, têm como função **/
private String registration;
private int registrationYear;
private double consumption;
private double autonomy;
private int cilinderCapacity;
/**
* Final variables. They are final because they do not suffer any kind of modification during the project.
* YEAR_OMISSION is 2016 because the currect year is 2016.
* ENVIRONMENTAL_CHARGE_OMISSION is 0.10(10 cents), gave this value because there is nothing to mention the
especific value, hence why I gave 0.10.
* RATING_RECENT = Is a string, just has the text "RECENT" inside.
* RATING_COMTEMPORY - Another string, just with the "Comtempory" text inside.
* RATING_CLASSIC - Yet again another string, with the "Classic" text.
* AGE_RECENT - It is to help to compare if a vehicle is recent or not, it has the value 3.
* AGE_CLASSIC - It is to again help to compare, value is 20.
*/
private static final int YEAR_OMISSION = 2016;
private static final double ENVIRONMENTAL_CHARGE_OMISSION=0.10;
private static final String RATING_RECENT="Recent";
private static final String RATING_CONTEMPORY="Contempory";
private static final String RATING_CLASSIC="Classic";
private static int AGE_RECENT=1;
private static final int AGE_CLASSIC=20;
/**
* Constructor of the object, it has the Registration
* #param registration
* #param registrationYear - The year the vehicle was first registered.
* #param consumption - How many liters the vehicle consumes.
* #param autonomy - How many KMs a vehicle can go without refuelling.
* #param cilinderCapacity - How many Cubic Inches the engine has.
*/
public Vehicle(String registration,int registrationYear, double consumption, double autonomy, int cilinderCapacity) {
this.registration = registration;
this.registrationYear = registrationYear;
this.consumption = consumption;
this.autonomy = autonomy;
this.cilinderCapacity = cilinderCapacity;
}
/**
* Null Constructor, it has no values, they will be attributed in the MAIN Class.
*/
public Vehicle() {
this.registration = "";
this.registrationYear = 0;
this.consumption = 0;
this.autonomy = 0;
this.cilinderCapacity =0;
this.registrationYear = YEAR_OMISSION;
}
/**
* Copy Constructor.
*/
public Vehicle(Vehicle vehicle) {
this.registration = vehicle.getRegistration();
this.registrationYear = vehicle.getRegistrationYear();
this.consumption = vehicle.getConsumption();
this.autonomy = vehicle.getAutonomy();
this.cilinderCapacity = vehicle.getCilinderCapacity();
}
public String getRegistration() {
return registration;
}
public int getRegistrationYear() {
return registrationYear;
}
public double getConsumption() {
return consumption;
}
public double getAutonomy() {
return autonomy;
}
public int getCilinderCapacity() {
return cilinderCapacity;
}
public double getYearRecent() {
return AGE_RECENT;
}
public double getAgeRecent(){
return AGE_RECENT;
}
public void setRegistration(String registration) {
this.registration = registration;
}
public void setRegistrationYear(int registrationYear) {
this.registrationYear = registrationYear;
}
public void setConsumption(double consumption) {
this.consumption = consumption;
}
public void setAutonomy(double autonomy) {
this.autonomy = autonomy;
}
public void setCilinderCapacity(int cilinderCapacity) {
this.cilinderCapacity = cilinderCapacity;
}
public void setAgeRecent() {
Vehicle.AGE_RECENT = 3;
}
/**
* Calculate the age of the vehicle to compare in the vehicleRating method
* #return The year, which is 2016 minus the year the vehicle was first registered.
*/
private int calculateAge(){
return YEAR_OMISSION-this.registrationYear;
}
/**
* Calculate the Circulation Tax.
* #return Returns the value of the Environmental Charge multiplied by the Cilinder Capacity of the vehicle.
*/
public double calculateCirculationTax(){
return ENVIRONMENTAL_CHARGE_OMISSION*cilinderCapacity;
}
/**
* Classify the vehicle based on the age.
* If the result given by the calculateAge method is minor than the AGE_RECENT variable(3), then it will
return "Recent"
* If the result is between Age_RECENT and AGE_CLASSIC(20), then it will say "Contemporary"
* If none of the IFs apply, it will return "Classic".
**/
public static String vehicleRating(Vehicle vehicle) {
if(vehicle.calculateAge() < Vehicle.AGE_RECENT) {
return Vehicle.RATING_RECENT; }
else if ((vehicle.calculateAge()>=Vehicle.AGE_RECENT)&&(vehicle.calculateAge()<=Vehicle.AGE_CLASSIC)){
return Vehicle.RATING_CONTEMPORY;}
else
return Vehicle.RATING_CLASSIC;
}
#Override
public String toString() {
return "Vehicle{" + "registration=" + registration + ", registrationYear=" + registrationYear + ", consumption=" + consumption + ", autonomy=" + autonomy + ", cilinderCapacity=" + cilinderCapacity + '}';
}
}
A setter that takes no arguments is simply a method, not a setter. In order to work as a setter a method must take a parameter that matches the type of the value being set - in your case, that would be int:
public static void setAgeRecent(int age) {
AGE_RECENT = age;
}
Note a few things here:
Since AGE_RECENT is static, setAgeRecent should be static
Since AGE_RECENT and setAgeRecent are static members of the same class Vehicle, you do not need to qualify AGE_RECENT with Vehicle
Now users of your class would be able to call your static setter as follows:
Vehicle.setAgeRecent(3);
A static varible, or class variable, may be used without the need to create an instance of that class. But its value may be changed freely at runtime.
A final variable is not a variable in a true sense, because it's value can't be changed at runtime.
Thus, you may have a set method for a static variable, but never to a final variable.
How can I sort a vector of my custom object and choose which property to sort by?
I did see this question & answer but I'm not too sure what its sorting it based on. Code example would be prefered to "methodology".
Sort a Vector of custom objects
public class ItemLocation {
String icon;
String title;
String message;
String subtext;
String deviceId;
double latCoords;
double lngCoords;
int expiary;
int id;
double proximity;
String locSeen;
}
Below is a example that will allow you to sort by a specified field of ItemLocation:
public void sort(final String field, List<ItemLocation> itemLocationList) {
Collections.sort(itemLocationList, new Comparator<ItemLocation>() {
#Override
public int compare(ItemLocation o1, ItemLocation o2) {
if(field.equals("icon")) {
return o1.icon.compareTo(o2.icon);
} if(field.equals("title")) {
return o1.title.compareTo(o2.title);
} else if(field.equals("message")) {
return o1.message.compareTo(o2.message);
}
.
. fill in the rest of the fields...
.
else if(field.equals("locSeen")) {
return o1.locSeen.compareTo(o2.locSeen);
}
}
});
}
See the JavaDocs for java.util.Comparable and java.util.Comparator.
A class that implements Comparable can be compared against other instances of that class. This is useful to implement a natural search order. To allow ordering other than the class's natural order you would need to implement a Comparator. A Comparator is a separate object that is capable of comparing two other objects using whatever criteria it wants.
In your case you'd probably want to implement a Comparator for each of the different properties that you want to order by, or one that can be configured.
Comparable and Comparator both use the same idea to determine ordering: A method returns less than 0, 0, or greater than 0 to inform the caller which of the 2 objects is ordered first. In the case of Comparable the first object is this.
This one works:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* ComparableDemo
* #author Michael
* #since 2/24/11
*/
public class ComparableDemo
{
public static void main(String[] args)
{
List<ItemLocation> itemLocations = new ArrayList<ItemLocation>();
for (String arg : args)
{
itemLocations.add(new ItemLocation(arg));
}
System.out.println("before sort: " + itemLocations);
Comparator<ItemLocation> comparator = new ItemLocationComparator();
Collections.sort(itemLocations, comparator);
System.out.println("after sort: " + itemLocations);
}
}
class ItemLocation
{
String icon;
String title;
String message;
String subtext;
String deviceId;
double latCoords;
double lngCoords;
int expiary;
int id;
double proximity;
String locSeen;
ItemLocation(String message)
{
this("", "", message, "", "", 0.0, 0.0, 0, 0, 0.0, "");
}
ItemLocation(String icon, String title, String message, String subtext, String deviceId, double latCoords, double lngCoords, int expiary, int id, double proximity, String locSeen)
{
this.icon = icon;
this.title = title;
this.message = message;
this.subtext = subtext;
this.deviceId = deviceId;
this.latCoords = latCoords;
this.lngCoords = lngCoords;
this.expiary = expiary;
this.id = id;
this.proximity = proximity;
this.locSeen = locSeen;
}
#Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append("ItemLocation");
sb.append("{message='").append(message).append('\'');
sb.append('}');
return sb.toString();
}
}
class ItemLocationComparator implements Comparator<ItemLocation>
{
public int compare(ItemLocation o1, ItemLocation o2)
{
return o1.message.compareTo(o2.message);
}
}
Here's the output:
C:\JDKs\jdk1.6.0_21\bin\java -Didea.launcher.port=7534 "-Didea.launcher.bin.path=C:\Program Files\JetBrains\IntelliJ IDEA 10.0.2\bin" -Dfile.encoding=windows-1252 com.intellij.rt.execution.application.AppMain ComparableDemo zeb meme apple
before sort: [ItemLocation{message='zeb'}, ItemLocation{message='meme'}, ItemLocation{message='apple'}]
after sort: [ItemLocation{message='apple'}, ItemLocation{message='meme'}, ItemLocation{message='zeb'}]
Process finished with exit code 0
Let's say we have a class with an int and a string. I can define how one object of that class may be compared against other.
I could choose any criteria. For instance, I may decide to sort based on the int. If I happen to have two int's with the same value, I may decide the string as an additional criteria, something like this:
// this class *knows* how to "compare" against him self
class CustomObject implements Comparable<CustomObject> {
String aString;
int aInt;
...
public int compareTo(CustomObject two ) {
int diff = this.aInt - two.aInt;//<-- compare ints
if( diff != 0 ) { // they have different int
return diff;
}
return this.aString.compareTo( two.aString );//<-- compare strings...
}
...
}
Here's a complete running demo ...
import java.util.*;
class SortDemo {
public static void main( String ... args ) {
// create a bunch and sort them
List<CustomObject> list = Arrays.asList(
new CustomObject(3, "Blah"),
new CustomObject(30, "Bar"),
new CustomObject(1, "Zzz"),
new CustomObject(1, "Aaa")
);
System.out.println( "before: "+ list );
Collections.sort( list );
System.out.println( "after : "+ list );
}
}
// this class *knows* how to "compare" against him self
class CustomObject implements Comparable<CustomObject> {
String aString;
int aInt;
CustomObject( int i, String s ) {
aInt = i;
aString = s;
}
// comparable interface lets you
// specify "HOW" to compare two
// custom objects
public int compareTo(CustomObject two ) {
// I migth compare them using the int first
// and if they're the same, use the string...
int diff = this.aInt - two.aInt;
if( diff != 0 ) { // they have different int
return diff;
}
// else let the strings compare them selves
return this.aString.compareTo( two.aString );
}
public String toString(){
return "CustomObject[aInt="+aInt+", aString="+aString+"]";
}
}
Here's the output:
before: [CustomObject[aInt=3, aString=Blah], CustomObject[aInt=30, aString=Bar], CustomObject[aInt=1, aString=Zzz], CustomObject[aInt=1, aString=Aaa]]
after : [CustomObject[aInt=1, aString=Aaa], CustomObject[aInt=1, aString=Zzz], CustomObject[aInt=3, aString=Blah], CustomObject[aInt=30, aString=Bar]]
I hope that's clear enough
You can also pass a custom comparator. Let me know if you need a sample of that.