ln9187
ln9187

Reputation: 740

Java 8 Casting Optional<Object> to long, int, String or Foo

I have this function and it returns an object because it has no ideas what the return could be. It could be in long, int, String, or Foo

@SuppressWarnings("unchecked")
public Optional<Object> getRequestByFieldName(String fieldName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Class<?> classA = instanceA.getClass();

    Method method = classA.getMethod("getAllFields");

    Object o =  method.invoke(instanceA);

    // I'm using protobuf to get the field value,
    // but a field could be missing because someone didn't send it
    // in that case I return an Optional Empty object
    Map<Descriptors.FieldDescriptor, Object> objectMap = (Map) o;

    Object fieldValue =  objectMap.entrySet().stream()
            .filter(fieldDescriptorObjectEntry -> {
                Descriptors.FieldDescriptor descriptor = fieldDescriptorObjectEntry.getKey();
                return descriptor.getName().equals(fieldName);
            })
            .findFirst().map(fieldDescriptorObjectEntry -> fieldDescriptorObjectEntry.getValue())
            .orElse(Optional.empty());

    return Optional.of(fieldValue);
}

How do I use this function to assign a variable to an int, long or Foo? This is what I currently have:

long someUid = (long) getRequestByFieldName("some_uid").orElse(0);

However, I keep getting this error if the field was missing:

java.util.Optional cannot be cast to java.lang.Long

Upvotes: 1

Views: 6199

Answers (1)

Slaw
Slaw

Reputation: 46181

If the field is not present you end up assigning Optional.empty() to the fieldValue variable.

Object fieldValue =  objectMap.entrySet().stream()
        .filter(fieldDescriptorObjectEntry -> {
            Descriptors.FieldDescriptor descriptor = fieldDescriptorObjectEntry.getKey();
            return descriptor.getName().equals(fieldName);
        })
        .findFirst().map(fieldDescriptorObjectEntry -> fieldDescriptorObjectEntry.getValue())
        .orElse(Optional.empty()); // ISSUE HERE

You then return with return Optional.of(fieldValue). This means, when no field is found, you return an Optional<Optional<?>> so when doing the following:

long someUid = (long) getRequestByFieldName("some_uid").orElse(0);

The orElse bit is never called because the Optional is never empty. Instead, it holds an Optional which obviously can't be cast to Long. From your code, you should be able to simply return the result of map, like so:

return objectMap.entrySet().stream()
        .filter(fieldDescriptorObjectEntry -> {
            Descriptors.FieldDescriptor descriptor = fieldDescriptorObjectEntry.getKey();
            return descriptor.getName().equals(fieldName);
        })
        .findFirst()
        .map(fieldDescriptorObjectEntry -> fieldDescriptorObjectEntry.getValue());

Whether or not this completely solves the casting problem depends on how you determine what type of object the Optional holds.

Upvotes: 4

Related Questions