简体   繁体   中英

How can I force Java to accept a conditional type for one of the parameters of a method call?

This question is hard to phrase, so I'm going to have to use some code samples. Basically, I have an (overloaded) method that takes 3 parameters, the last of which I overloaded. As in, sometimes the last parameter is a String , sometimes it's a double , etc. I want to call this method using a ternary conditional expression as the last parameter, so that depending on a certain value, it will pass either a double or a String. Here's an example...

Overloaded method headers:

writeToCell(int rowIndex, int colIndex, double value)

writeToCell(int rowIndex, int colIndex, String value)

What I'm trying to do:

writeToCell(2, 4, myValue != null ? someDouble : someString);

This, however, causes a compilation error:

The method writeToCell(int, int, double) in the type MyType is not applicable 
for the arguments (int, int, Object&Comparable<?>&Serializable)

It seems that Java isn't "smart enough" (or just doesn't have the functionality built in on purpose) to realize that either option has a method that supports it. My question is - is there any way to do this? I know I can sort of simulate it by passing in the double as a String (eg writeToCell(2, 4, myValue != null ? someDouble.toString() : someString); ), but the method needs to receive it as a double data type.

Logic tells me that there's no way to force Java to accept this statement... But it's worth a try, as it will result in a lot clearer code for me. Anyone know of a way to do this...?

Method calls are resolved statically, at compile time, not dynamically, at runtime. Therefore, the compiler is looking for the type that matches both of the possible arguments in that expression, as you can see.

You could do what the SDK does a lot of, and define your method as

writeToCell(int rowIndex, int colIndex, Object value)

and then have the first line be

final String repr = String.valueOf(value);

There are plenty of other solutions, but it's best not to overthink this.

Demonstration:

static class WrongAgain
{
    void frob(final Object o)
    {
        System.out.println("frobo " + o);
    }

    void frob(final String s)
    {
        System.out.println("frobs " + s);
    }

}

public static void main(final String[] args)
{
    final WrongAgain wa = new WrongAgain();
    wa.frob("foo");
    Object o = "foo";
    wa.frob(o);
}

If method lookup were dynamic, then the you'd see "frobs" both times. It's static.

Is there any particular reason not to just write it out?

if (myValue == null)
  writeToCell(2, 4, someString);
else
  writeToCell(2, 4, someDouble);

I suspect that it chokes up because the type of object (and hence overloaded version which should be called) cannot be determined at compile time. It's similar to:

Object o = myValue != null ? someDouble : someString;
writeToCell(2, 4, o);

Thanks everyone for all your suggestions and explanations.

A coworker of mine took a look at my code and suggested a solution that I implemented. I inserted the following code into the beginning of my double method:

if(value == null){
   writeToCell(rowIndex, colIndex, someString)
}
else{
   ...method body... 
}

I know that this implementation might not always be the best idea, but since I almost always need to check for null when passing a double value to this method, it makes the most sense in this situation. This way I don't have to repeat the check code (if / ternary statement) each time, and my method calls are much cleaner.

The return type from the ternary operation is being upcast to java.lang.Object, as the common superclass class of both String and the autoboxed double, so you would have to provide the method signature for the object, and then use that to make the appropriate cast and call the relevant method:

public class SoCast {


 public static void main(String[] args) {
  SoCast sc = new SoCast();

  Double someDouble = 3.14;
  String someString = "foo";
  String myValue = null;

  sc.writeToCell(2, 4, myValue != null ? someDouble : someString);

  myValue = "hello";

  sc.writeToCell(2, 4, myValue != null ? someDouble : someString);

 }

 private int writeToCell(int rowIndex, int colIndex, Object value) {
  int result = -1;
  if (value instanceof String) {
   result = this.writeToCell(rowIndex, colIndex, (String) value);
  } else if (value instanceof Double) {
   result = this.writeToCell(rowIndex, colIndex, (Double) value);
  }
  return result;
 }

 private int writeToCell(int rowIndex, int colIndex, Double value) {
  return 1;
 }

 private int writeToCell(int rowIndex, int colIndex, String value) {
  return 5;
 }
}

The ternary operator has to return the same type from the "then" or "else" clause. If you can deal with Double instead of double , then you could try

Object o = (myValue != null ? someDouble : someString);
writeToCell(2, 4, o);

and change the writeToCell method to accept arguments (int, int, Object) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM