2

I am trying to throw a custom exception using ternary operation and orElseThrow as shown below:

public static MainProviderType getMainProviderType(ProviderType providerType) {
    return Optional.ofNullable(MainProviderType.valueOf(providerType.name()))
        .orElseThrow(() -> new ProviderTypeNotFoundException(providerType.name()));
}

However, when providerType is null, this method returns NullPointerException rather than ProviderTypeNotFoundException. I think the problem is related to that; it cannot evaluate MainProviderType.valueOf(providerType.name()) because providerType.name(). Is that true? And how should I use it and fix the problem?

knittl
  • 246,190
  • 53
  • 318
  • 364
Jack
  • 1
  • 21
  • 118
  • 236
  • 2
    If your `providerType` is null then `providerType.name()` will always throw the NullPointerException – Nagaraju Chitimilla Aug 13 '21 at 06:02
  • Does this answer your question? [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – knittl Aug 13 '21 at 06:07
  • @NagarajuChitimilla I know that – Jack Aug 13 '21 at 06:14
  • Is ProviderType a part of MainProviderType? Does the names from ProviderType exist on MainProviderType? – Tomas F Aug 13 '21 at 06:25

3 Answers3

3
public static MainProviderType getMainProviderType(ProviderType providerType) {
    return Optional.ofNullable(providerType)
    .map(ProviderType::name)
    .map(MainProviderType::valueOf)
    .orElseThrow(() -> new ProviderTypeNotFoundException("some message"));
}

Update Full example with test case

    private enum ProviderType {
        ONE,
        TWO
    }

    private enum MainProviderType {
        ONE,
        TWO,
        THREE
    }

    public MainProviderType getMainProviderType(ProviderType providerType) {
        return Optional.ofNullable(providerType)
            .map(ProviderType::name)
            .map(MainProviderType::valueOf)
            .orElseThrow();
    }

    @Test
    void selectMainProviderType() {
        var mainProviderType = getMainProviderType(ProviderType.ONE);

        assertEquals(MainProviderType.ONE, mainProviderType);
    }
Tomas F
  • 7,226
  • 6
  • 27
  • 36
  • Won't that last dereference of providerType still throw an NPE? – tgdavies Aug 13 '21 at 06:03
  • Yes you're right. Too quick to answer :-) Thanks – Tomas F Aug 13 '21 at 06:05
  • Thanks a lot. I tried but have a little problem related to **valueOf**: "Reference to 'valueOf' is ambiguous, both 'valueOf(String)' and 'valueOf(Class, String)' match" – Jack Aug 13 '21 at 06:25
  • I think I should add `.name()` like `return Optional.ofNullable(providerType.name()).map(MainProviderType::valueOf)` – Jack Aug 13 '21 at 06:26
  • You need to do that after checking if providerType is null somehow, otherwise you will end up in a NullPointerException – Tomas F Aug 13 '21 at 06:30
  • An other question. I noticed you have ProvderType and MainProviderType - does all names from ProviderType exist on MainProviderType? – Tomas F Aug 13 '21 at 06:31
  • Do you mean that using `Optional.ofNullable(providerType.name()).map(MainProviderType::valueOf)` is not true? In this case, how to fix the error I mentioned in my previous comment? – Jack Aug 13 '21 at 06:34
  • For your question, yes thats's true. – Jack Aug 13 '21 at 06:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235962/discussion-between-tomas-f-and-rosa). – Tomas F Aug 13 '21 at 06:42
  • Your last update seems to work, is not it? – Jack Aug 13 '21 at 06:50
  • @TomasF And What about using `ProviderType.class.getSimpleName()` instead of `providerType.name()`. Because when `providerType` is null, it is meaningless to try to get its name value. So, is it logical for passing name to the custom exception? – Jack Aug 13 '21 at 06:54
  • I tried to start a chat before :-) getSimpleName would give you name name of the actual class, not the name of the enum. I'm not really sure why it does not work in your circumstance, when i set up a test with the code above it works fine... – Tomas F Aug 13 '21 at 08:14
  • Could you please post the test methods with 2 methods (passing parameter and passing null)? – Jack Aug 13 '21 at 13:09
0

Your problem is related to the providerType.name(). If the providerType is null, then your code will work like this: null.name(). This makes no sense and a NullPointerException will always be thrown when providerType is null

You could check if the providerType is null before continuing to everything else:

if(providerType == null)
{
    throw ProviderTypeNotFoundException(..);
}
Renis1235
  • 4,116
  • 3
  • 15
  • 27
-1

orElseThrow() only works on the Optional's value. Your code might fail trying to create the Optional instance or might fail when constructing the exception by invoking .name() on a potential null object.. It could be rewritten by separating each expression into a statement, which gives the equivalent form:

var name = providerType.name(); // nullpointer! providerType can be null
var type = MainProviderType.valueOf(name); // nullpointer! name can be null
var optional = Optional.ofNullable(type);
return optional.orElseThrow(() -> new ProviderTypeNotFoundException(providerType.name())); // nullpointer! providerType can still be null

Either check for nullness of providerType and its name or create the Optional earlier and then apply each transformation via map:

public static MainProviderType getMainProviderType(ProviderType providerType) {
    return Optional.ofNullable(providerType)
            .map(ProviderType::name)
            .map(MainProviderType::valueOf)
            .orElseThrow(() -> new ProviderTypeNotFoundException(providerType != null ? providerType.name() : "no name"));
}
knittl
  • 246,190
  • 53
  • 318
  • 364
  • Name can only be null in the first code block if MainProviderType would not be an enum right? – Tomas F Aug 13 '21 at 06:09
  • @TomasF I don't understand your question. `name` could be null and assuming `MainProviderType` is an enum, its `valueOf` method will throw a NullPointerException. Is that what you are asking? – knittl Aug 13 '21 at 06:12
  • No. In the first code block - if provderType is not null, and MainProviderType is an enum, then providerType.name() would not be able to return null right? So the second line could only give a NPE in cases where MainProviderType is not an enum. (but in this case it probably is) – Tomas F Aug 13 '21 at 06:19
  • @Rosa that's for you to find out, it is your code. I cannot run nor test it. With the limited information provided, it is very likely to throw a NullPointerException in one of the commented expressions in my answer. Best to start up a debugger, set a breakpoint and step through the code to see where exactly the exception is thrown. – knittl Aug 13 '21 at 06:32
  • What about using `ProviderType.class.getSimpleName()` instead of `providerType.name()`. Because when `providerType` is null, it is meaningless to try to get its name value. So, is it logical for passing name to the custom exception? – Jack Aug 13 '21 at 06:53
  • @Rosa `ProviderType.class.getSimpleName()` will return the `"ProviderType"`. If you need that, you can directly write the string literal `"ProviderType"` directly in your code. `providerType.name()` returns the whatever the method `name()` returns on this specific object _instance_ of the class. – knittl Aug 13 '21 at 15:26