Closed rfc2822 closed 2 years ago
Yes, the problem does occur in plain Java! Thank you for pointing that out.
That code sample from the FAQ only works if you leave out the generics and ignore the type safety warnings.
In fact, similar code is used by the writer classes (VCardWriter
, JCardWriter
, etc). Generics are left out of the VCardPropertyScribe scribe
variable declaration. A method-level annotation is used to suppress the warnings.
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void _write(VCard vcard, List<VCardProperty> properties) throws IOException {
//...
for (VCardProperty property : propertiesToAdd) {
VCardPropertyScribe scribe = index.getPropertyScribe(property);
String value = scribe.writeText(property, context);
//...
}
//...
}
I understand why the compiler has a problem with this. The compiler has no way of knowing that the VCardPropertyScribe
implementation correctly corresponds with the given VCardProperty
implementation. For example, in the code below, a FormattedName
property object is passed into a AddressPropertyScribe
object. The compiler gives several warnings, but allows the code to be compiled and run. When the code is run, the last line throws a ClassCastException because it is trying to cast the FormattedName
object to an Address
object.
ScribeIndex index = new ScribeIndex();
WriteContext context = new WriteContext(VCardVersion.V3_0, null, true);
VCardProperty property = new FormattedName("value");
VCardPropertyScribe scribe = index.getPropertyScribe(Address.class); //compiler: raw type warning
String value = scribe.writeText(property, context); //compiler: type safety warning; ClassCastException thrown at runtime
ez-vcard never throws this exception because the code is written such that the property object that is passed into the index.getPropertyScribe
method is ALSO passed into the scribe.writeText
method. It's never a different object, as it is in the failing code sample above.
One potential fix is to change the method signature of VCardPropertyScribe.writeText
to accept a VCardProperty
object instead of a <T extends VCardProperty>
object. However, doing that prevents the scribe classes from being able to use their corresponding property classes in each of their "write" methods (e.g. using the Address
class in the AddressPropertyScribe.writeText
method signature.
At the moment, I am not sure what else can be done to resolve this. I am open to ideas of course!
Thanks for your explanations! I have found how to do the same unchecked conversion with Kotlin:
val vCard: VCard
val scribeIndex = ScribeIndex()
for (prop in vCard.properties) {
// prop is now of type VCardProperty
@Suppress("UNCHECKED_CAST")
val scribe = scribeIndex.getPropertyScribe(prop) as VCardPropertyScribe<VCardProperty>
// scribe is now of type VCardPropertyScribe<VCardProperty>
scribe.writeJson(prop) // works!
}
Hi,
I want to serialize some
VCardProperty
s because they're unknown on an application level and should later be inserted when creating the vCard again. In the FAQ, I can find:However, this doesn't work because
property
is of typeVCardProperty
andscribe
is then of typeVCardPropertyScribe<out VCardProperty>
(Java:VCardPropertyScribe<? extends VCardProperty>
).However,
property
doesn't necessarily extend VCardProperty, but is a VCardProperty. So the Kotlin compiler changes the signature of the examples'sscribe.writeText
towriteText(property: Nothing!)
:Is that intended behavior or some Java/Kotlin incompatibility? As I understand it, the problem should occur for plain Java, too.
How is that intended to be used?