0

Let's say I have a class and method like this:

public class Example {

    private final Map<String, Object> stuff = new HashMap<>();

    public <T> T getValue(String key) {
        return (T) stuff.get(key);
    }
}

Is it possible to force someone writing code that calls getValue() to explicitly provide a type witness to the method call?

Example example = new Example();
Boolean resultWithParameter    = example.<Boolean>getValue("some_key");

What I'd like to do is always require the parameter when calling the method. Is there a way to do that? I know I could always add a Class parameter but I was exploring other options.

David DeMar
  • 2,390
  • 2
  • 32
  • 45
  • 2
    That's called a type witness – Michael Mar 09 '20 at 14:51
  • Not sure what you're saying. If you want a certain return type, then you specify the type. Anytime you use a single type parameter `` like this, it's equivalent to just saying `Object` so you abdicated your chance to require a type by declaring it that way. Declare it with the type you want instead. – markspace Mar 09 '20 at 14:52
  • 3
    And I've never heard the term "type witness" before, so I'd appreciate an explanation of that term. – markspace Mar 09 '20 at 14:55
  • 2
    @markspace [Type Witness in java generics](https://stackoverflow.com/questions/24932177/type-witness-in-java-generics) – azurefrog Mar 09 '20 at 14:57
  • 3
    @markspace From Oracle tutorials https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html – Michael Mar 09 '20 at 14:57
  • 1
    I've never heard it called a "type witness" before. I learned something new today. – David DeMar Mar 09 '20 at 15:00
  • Yup, me too actually. Thanks @Michael – markspace Mar 09 '20 at 15:00
  • @Michael But "type witness" is not the official term and is not mentioned anywhere in the Java Language Specification. The JLS calls them "[**type arguments**](https://docs.oracle.com/javase/specs/jls/se13/html/jls-4.html#jls-4.5.1)", whether as `List` or `example.getValue()` doesn't matter, the `` part is the *type arguments*, similar to `(expr, ...)` being *method arguments*. – Andreas Mar 09 '20 at 15:42
  • @Andreas I was having a think about this. I think the reason that the JLS omits it is because it has no reason to make the distinction. If the JLS used both terms, there would be the potential for the behaviour to vary between the two. It is therefore preferable to only use the broadest applicable term. In the real world, the term 'type witness' holds additional semantic value because it refers to type arguments used in a specific way. Therefore I don't think it is correct to assert - if it is what you're asserting - that the term is "not official" simply because the JLS doesn't define it. – Michael Mar 09 '20 at 15:57

1 Answers1

1

There isn't a way to enforce it. Here is the structure of a method invocation as defined by the JLS.

MethodInvocation:
  MethodName ( [ArgumentList] )
  TypeName . [TypeArguments] Identifier ( [ArgumentList] )
  ExpressionName . [TypeArguments] Identifier ( [ArgumentList] )
  Primary . [TypeArguments] Identifier ( [ArgumentList] )
  super . [TypeArguments] Identifier ( [ArgumentList] )
  TypeName . super . [TypeArguments] Identifier ( [ArgumentList] )
ArgumentList:
  Expression {, Expression}

Specifically, your example refers to this one

Primary . [TypeArguments] Identifier ( [ArgumentList] )

example . <Boolean>       getValue   ( "some_key"     );

If you are not familiar with the notation, the most important part is this:

The syntax [x] on the right-hand side of a production denotes zero or one occurrences of x. That is, x is an optional symbol

So where [TypeArguments] (a type witness) is applicable, it is always optional; there is no way to enforce that it is present.

Michael
  • 41,989
  • 11
  • 82
  • 128