-
Notifications
You must be signed in to change notification settings - Fork 18
Enhance type safety when getting tag values #63
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
Conversation
This removes all uses of `ReadableHeader::getValue` which returns `Object` and replaces them with methods that return specific types. This simplifies the code and eliminates the need for the caller to cast the result. The method `RpmBaseTag::getDataType` was added to store the tag's data type. The tag's data type is checked when the various `ReadableHeader` methods are called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's an overall improvement. However, I think that the way generics/type arguments are being used isn't how Java works. I think we need some more work in this to provide some more type safety.
result.add(new Dependency(name, version, flagSet)); | ||
} | ||
return result; | ||
final List<String> names = header.getStringList(namesTag); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we make some breaking changes on how this function behaves. Which is ok. However, I also think we should document that.
So far, to my understanding, the function simply ignored unexpected values and types. Now it will throw an exception. And while it's a private method, I think it makese sense adding a note and tracing calls that lead to this call and put a note on the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the change is to throw the IllegalArgumentException
instead of ignoring it. You mean to add javadoc for it (although it's an unchecked exception)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the current javadoc acceptable? I put it in the main text now as @throws
because it's unchecked, but I wasn't sure about that.
rpm/src/main/java/org/eclipse/packager/rpm/parse/HeaderValue.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
public Object getTagOrDefault(final T tag, final Object defaultValue) { | ||
return getOptionalTag(tag).orElse(defaultValue); | ||
@SuppressWarnings("unchecked") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for the next method, I think we should add an actual type check. IIRC, you can use dataType.cast(..)
for this. However IIRC using (HeaderValue<E>
) won't work, because that's simply being erased and becomes HeaderValue
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will try to use .cast()
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, now we're just returning HeaderValue
with no type, so there's nothing to cast, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still some inconsistency in my current code which still has:
public <E> Optional<HeaderValue> getOptionalTag(final T tag, Class<E> dataType);
private Optional<HeaderValue> getEntry(final int tag, Class<?> dataType);
Are you sure nothing is gained by HeaderValue<?>
, then we'd have the type <E>
, which says that the type in header value should match the type in the class argument.
<E> Optional<HeaderValue<E>> getOptionalTag(final T tag, Class<E> dataType);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using:
<E> Optional<HeaderValue<E>> getOptionalTag(final T tag, Class<E> dataType);
would actually have a benefit if the type of the value really is being limited to E
. That requires casting using dataType.cast(..)
. It would also be required to handle the case when the value is not of type E
. Throw an exception, return None
would both work.
The question is just if that makes the code calling into it much better?
Given Java's new pattern type construct (not sure I got the name right) one should be able to do something like:
if (headers.getEntry(Tags.MyTag) instanceof String s) {
// do something with s
}
When reading, the header can basically carry anything, and we can't control that. Only when creating a header, we could enforce (via the type system) that we user cannot create wrong headers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would actually have a benefit if the type of the value really is being limited to
E
. That requires casting usingdataType.cast(..)
. It would also be required to handle the case when the value is not of typeE
. Throw an exception, returnNone
would both work.
My thoughts:
Adding a type parameter, say <E>
, only seems to change two methods, getEntry
and getOptionalTag
, to take a Class<E>
parameter. For the datatype.cast()
, this doesn't work because the datatype is E
, but we need HeaderValue<E>
?
Also, now we're adding a type parameter to HeaderValue
which never gets used by the class itself. This is different from adding it to RpmTagValue
, where it is actually used.
This removes all uses of
ReadableHeader::getValue
which returnsObject
and replaces them with methods that return specific types. This simplifies the code and eliminates the need for the caller to cast the result.The method
RpmBaseTag::getDataType
was added to store the tag's data type. The tag's data type is checked when the variousReadableHeader
methods are called.