Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 176
  • Last Modified:

Java custom types and subsets of types

I have a bunch of unit types:
package units;

public enum Units {
  FAHRENHEIT,
  CELSIUS,
  KELVIN,
  FEET,
  INCHES,
  MILES,
  METERS;
}

Open in new window

and unit categories:
public enum TemperatureUnits = { FAHRENHEIT, CELSIUS, KELVIN }

Open in new window

public enum LengthUnits = {  FEET, INCHES, MILES, METERS }

Open in new window

Problem is as you can see all of my values are defined twice, and I'm sure that's going to lead to endless confusion.

I've also considered using a hierarchy of classes:

Units

    TemperatureUnits extends Units
    LengthUnits extends Units

        Fahrenheit extends TemperatureUnits
        Celsius extends TemperatureUnits
        Kelvin extends TemperatureUnits

        Inches extends LengthUnits
        Feet extends LengthUnits
        Miles extends LengthUnits
        Meters extends LengthUnits

Then I can do:
double value = myMeasurement.getValueAs(new Meters());

Open in new window

but it seems silly to create a class instance new Meters() when all I really need is the class type.

I'd like to be able to specify what range of units a method can accept:
    public double getValueAs(LengthUnits u) {
...
    public double getValueAs(TemperatureUnits u) {
....
    public double manipulate(Units u) {
....

Open in new window

Any suggestions?
0
deleyd
Asked:
deleyd
  • 3
  • 2
3 Solutions
 
dpearsonCommented:
Although you can't extend an enum to create a hierarchy you can implement an interface.

So how about this structure:

public interface Units {
}

public enum LengthUnits implements Units {
	FEET, INCHES, MILES ;

	public double convertToMeters(double value) {
                // Yes this math is a bit off...
		switch (this) {
			case FEET: return value / 3.0 ;
			case INCHES: return value / 36.0 ;
			case MILES: return value * 8000.0 / 5.0 ;
			default: throw new IllegalStateException("Unknown value in enum") ;
		}
	}
}

public enum TemperatureUnits implements Units {
	CELSIUS, FAHRENHEIT, KELVIN ;
}

Open in new window


Then you can write code like this:

            Units example = LengthUnits.FEET ;
            LengthUnits other = LengthUnits.INCHES ;

                // Convert 100 inches to meters
                double meters = LengthUnits.INCHES.convertToMeters(100)  ;

together with your methods taking Units or LengthUnits etc.

You can also define methods in the Units interface, which would need to be implemented by the enums - just like for a class - if you'd like to be able to call a method on a Units object.

Any good?

Doug
0
 
deleydAuthor Commented:
Hmm, interesting.

How could I use this to pass a parameter to a method specifying what unit type I would like the return value to be in? The method would be in a different class.

Some methods can accept any unit, while other methods want a specific subtype of unit (as in Length, Temperature,...)
0
 
dpearsonCommented:
The examples you posted earlier could all be written now.
If they're in different classes it would look like this:

public class A { public double getValueAs(LengthUnits u) ; }
public class B { public double getValueAs(TemperatureUnits u) ; }
public class C { public double manipulate(Units u) ; }

Here are some example calls:

A a = new A() :
a.getValueAs(LengthUnits.INCHES) ;

// Or we can use a variable if we prefer:
LengthUnits unitsForThisCalc = LengthUnits.MILES ;
a.getValueAs(unitsForThisCalc) ;

// This won't compile - it only takes LengthUnits
a.getValuesAs(TemperatureUnits.CELSIUS) ;

// But these are all fine, since they accept any Units
C c = new C() ;
c.manipulate(TempatureUnits.CELSIUS) ;
c.manipulate(LengthUnits.FEET) ;

Doug
0
 
deleydAuthor Commented:
Thank you I think I see it now, I'll give it a try.

Somewhat related, is it true with Generics there's no way to create a Generic type T and pass parameters to the constructor? As in:
class MyClass<T>

public T myMethod() {
    int p1 = 5;
    String p2 = "Hello";

    T t = new T(p1,p2);  //not possible...
    return (t);
}

Open in new window

I've seen ways of creating type T passing no parameters to the constructor, but I don't think it's possible to create type T with parameters. Is that correct that it can't be done?
0
 
dpearsonCommented:
Yeah I don't think that's possible.
I'd be surprised if you can even call the default constructor (the one with no parameters).

I think the root issue is that in Java, the generic information is all discarded at runtime.
So there's no "T" left to instantiate at runtime (it's only available at compile time - but clearly the runtime would need it as well to call a constructor on the right class).


You should however be able to get the desired behavior through reflection.  You'd need to pass in both "T" and the class of T as a String.  Then you can call the constructor for that class, passing parameters etc.  The code's a bit ugly and verbose but should work fine.  Here's a sample tutorial on how to do this:
http://tutorials.jenkov.com/java-reflection/constructors.html

You'd end up instantiating the class with something like

MyClass<Integer, Integer.class.toString()) <-- second parameter passes in the name of the class so you can instantiate it at runtime.  The first parameter you'd use for type checking like normal.

Doug
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now