Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to resolve a Quantity instance for a Number and Unit #201

Open
msqr opened this issue Sep 26, 2019 · 4 comments
Open

How to resolve a Quantity instance for a Number and Unit #201

msqr opened this issue Sep 26, 2019 · 4 comments

Comments

@msqr
Copy link

msqr commented Sep 26, 2019

Hello, I am not finding how to resolve a Quantity<?> instance if I am starting with a Number and a Unit<?> instance, using only the javax.measure API. The trouble here is that I don't know the Quantity class at runtime, so I can't call javax.measure.spi.ServiceProvider.getQuantityFactory(Class<Q> quantity) to get the appropriate QuantityFactory on which I could call javax.measure.spi.QuantityFactory.create(Number value, Unit<Q> unit).

I don't know there is a very easy way using Indriya: tech.units.indriya.quantity.Quantities.getQuantity(Number value, Unit<Q> unit) but I am trying to understand how to achieve the same thing with only the javax.measure API. Could you point me in the right direction?

@keilw
Copy link
Member

keilw commented Sep 26, 2019

Matt, Thanks a lot for your question. You are scratching an older itch I'm afraid and the Java language itself has no real solution here at runtime, see a recent analysis: https://stackify.com/jvm-generics-type-erasure/ None of the solutions work really well without either passing the type in every case (instead of a class this is also what ICU4J does) or being restricted to a special condition like inline code or static methods where the generic type may sometimes be available via Reflection.

All in all, that is a question for future Java releases, Project Valhalla (I did some very early experiments with it here: https://github.com/unitsofmeasurement/uom-demos/tree/master/console/valhalla) may help, but it is not yet clear, which aspects of it make it into a future JDK and which version. So I put a deferred label on it, we only finalized JSR 385, so it might have a bit of time anyway. Without changing the API we could try to help with that in the RI, e.g. AbstractSystemOfUnits, so please feel free to create a similar ticket in https://github.com/unitsofmeasurement/indriya/issues. That class has a getUnit(Class) method already, but it could be an option to add a getUnits(Class) method returning a Set or Collection like getUnits(Dimension) does there already. The existing method only returns the system or base unit for the given quantity type. We would have to store the meta-information like the type class for every unit (otherwise it would not be in such collection) but it could offer a way to retrieve all units for a given type until Java allows to get that information more explicitly from the Unit or Quantity itself.

@keilw
Copy link
Member

keilw commented Sep 26, 2019

Actually I think unitsofmeasurement/indriya#224 might be a ticket for that already. It's phrased very "thin" but I would interpret "list of all valid Dimension for a Quantity LENGTH..." as list of valid units for a Quantity Length, because a Unit only has one Dimension, and while LENGTH is also a Dimension instance, a method that returns all units for a given dimension like that already exists. It doesn't for a given quantity class because that meta-information is not stored so far.

@msqr
Copy link
Author

msqr commented Sep 26, 2019

Hi Werner, thanks for your response. I've dealt with erasure problems like this, I understand what you mean. I guess I was wishing the Unit API could provide the Quantity class directly, something like

Class<Q> getQuantityClass();

Which would of course require all Unit implementations to capture that class at construction time, as you note, for example in AbstractUnit

	private final Class<Q> quantityClass;

	/**
	 * Constructor.
	 * 
	 * @param quantityClass the quantity class.
	 */
	protected AbstractUnit(Class<Q> quantityClass) {
		this.quantityClass = quantityClass;
	}

	/**
	 * Constructor setting a symbol.
	 * 
	 * @param quantityClass the quantity class.
	 * @param symbol the unit symbol.
	 */
	protected AbstractUnit(Class<Q> quantityClass, String symbol) {
		this.quantityClass = quantityClass;
		this.symbol = symbol;
	}

	public Class<Q> getQuantityClass() {
		return quantityClass;
	}

I understand that impacts all implementations of Unit but does not impact consumers of the API (other that the added getQuantityClass() method). This would then continue to support Java 8.

This issue comes up for me on a project where I need to deal with arbitrary, user-configured units defined as strings so that I can later capture amount values associated with those units into Quantity instances and translate them into system or base units.

Thanks again for the info; it looks like for now I will need to use Indriya directly. Perhaps a tweak to the Unit API like discussed here could happen in the future.

@keilw
Copy link
Member

keilw commented Sep 27, 2019

I'm not sure if Class<Q> would work because the problem is, Q at runtime usually gets deleted even if you try to store the class, Class would but then one had to store e.g. Length etc. everywhere. For Unit because that comes from a registry like Units or another system, it could work, but it would be a redundancy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants