Cannot cast from [java.lang.String] to [char] error

Hello, I am currently learning painless and one of the exercises is to associate letter grades to a range of numbers. I have been stuck on a specific error which is:

{
  "error": {
    "root_cause": [
      {
        "type": "script_exception",
        "reason": "compile error",
        "script_stack": [
          """... {
			    letter_grade = 'A';
			  } 
			  else  ...""",
          "                             ^---- HERE"
        ],
        "script": """
			Map toLetterGrade(int numeric_grade){
			  Map grade = new HashMap();
			  char letter_grade;
			  if (numeric_grade >= 93 && numeric_grade <= 100) {
			    letter_grade = 'A';
			  } 
			  else if (numeric_grade >= 85 && numeric_grade <= 92) {
			    letter_grade = 'B';
			  } 
			  else if (numeric_grade >= 76 && numeric_grade <= 84) {
			    letter_grade = 'C';
			  } 
			  else if (numeric_grade >= 65 && numeric_grade <= 75) {
			    letter_grade = 'D';
			  } 
			  else if (numeric_grade >= 0 && numeric_grade <= 64) {
			    letter_grade = 'F';
			  } 
			  else {
			    throw new IllegalArgumentException();
			  }
			  grade.put(letter_grade, numeric_grade);
			  return grade;
			}
			return toLetterGrade(params.numeric_grade);
		""",
        "lang": "painless",
        "position": {
          "offset": 181,
          "start": 156,
          "end": 206
        }
      }
    ],
    "type": "script_exception",
    "reason": "compile error",
    "script_stack": [
      """... {
			    letter_grade = 'A';
			  } 
			  else  ...""",
      "                             ^---- HERE"
    ],
    "script": """
			Map toLetterGrade(int numeric_grade){
			  Map grade = new HashMap();
			  char letter_grade;
			  if (numeric_grade >= 93 && numeric_grade <= 100) {
			    letter_grade = 'A';
			  } 
			  else if (numeric_grade >= 85 && numeric_grade <= 92) {
			    letter_grade = 'B';
			  } 
			  else if (numeric_grade >= 76 && numeric_grade <= 84) {
			    letter_grade = 'C';
			  } 
			  else if (numeric_grade >= 65 && numeric_grade <= 75) {
			    letter_grade = 'D';
			  } 
			  else if (numeric_grade >= 0 && numeric_grade <= 64) {
			    letter_grade = 'F';
			  } 
			  else {
			    throw new IllegalArgumentException();
			  }
			  grade.put(letter_grade, numeric_grade);
			  return grade;
			}
			return toLetterGrade(params.numeric_grade);
		""",
    "lang": "painless",
    "position": {
      "offset": 181,
      "start": 156,
      "end": 206
    },
    "caused_by": {
      "type": "class_cast_exception",
      "reason": "Cannot cast from [java.lang.String] to [char]."
    }
  },
  "status": 400
}

For reference, my current code is:

POST _scripts/painless/_execute
{
	"script": {
		"lang": "painless",
		"source": """
			Map toLetterGrade(int numeric_grade){
			  Map grade = new HashMap();
			  char letter_grade;
			  if (numeric_grade >= 93 && numeric_grade <= 100) {
			    letter_grade = 'A';
			  } 
			  else if (numeric_grade >= 85 && numeric_grade <= 92) {
			    letter_grade = 'B';
			  } 
			  else if (numeric_grade >= 76 && numeric_grade <= 84) {
			    letter_grade = 'C';
			  } 
			  else if (numeric_grade >= 65 && numeric_grade <= 75) {
			    letter_grade = 'D';
			  } 
			  else if (numeric_grade >= 0 && numeric_grade <= 64) {
			    letter_grade = 'F';
			  } 
			  else {
			    throw new IllegalArgumentException();
			  }
			  grade.put(letter_grade, numeric_grade);
			  return grade;
			}
			return toLetterGrade(params.numeric_grade);
		""",
		"params": {
      "numeric_grade": 98
		}
	}
}	

I cannot understand what is causing the error and how to fix it, so any help/fix is appreciated! (FYI: the code does run if I change the data type to String, I am just curious why char is not working here :thinking:)

Hello @Simriti_Bundhoo

This is what i found : Even though 'A' looks like a Java char, Painless interprets it as a String.

POST _scripts/painless/_execute
{
  "script": {
    "lang": "painless",
    "source": """
      Map toLetterGrade(int numeric_grade){
        Map grade = new HashMap();
        char letter_grade;
        if (numeric_grade >= 93 && numeric_grade <= 100) {
          letter_grade = "A".charAt(0);
        } 
        else if (numeric_grade >= 85 && numeric_grade <= 92) {
          letter_grade = "B".charAt(0);
        } 
        else if (numeric_grade >= 76 && numeric_grade <= 84) {
          letter_grade = "C".charAt(0);
        } 
        else if (numeric_grade >= 65 && numeric_grade <= 75) {
          letter_grade = "D".charAt(0);
        } 
        else if (numeric_grade >= 0 && numeric_grade <= 64) {
          letter_grade = "F".charAt(0);
        } 
        else {
          throw new IllegalArgumentException();
        }
        grade.put(Character.toString(letter_grade), numeric_grade);
        return grade;
      }
      return toLetterGrade(params.numeric_grade);
    """,
    "params": {
      "numeric_grade": 98
    }
  }
}

Thanks!!

@Tortoise, Hey, is that the right link you meant to send? Because the documentation clearly says char is a primitive type, so I assume it’s a valid type and should work as expected. I don't see it mentioning anywhere that Painless interprets a single character as String despite it being declared as a char type.

Turning question around, where in the documentation does it say "AA" is a string but "A" is a char ? Rather it gives an explicit example to cast a one-char-long-string into a char.

String s = "s";
char c = (char)s;

This cut and paste directly from the painless documentation.

My version of your code, HTH!

POST _scripts/painless/_execute
{
  "script": {
    "lang": "painless",
    "source": """
      Map toLetterGrade(int numeric_grade){
        Map grade = new HashMap();
        char letter_grade;
        char myA=(char)"A";char myB=(char)"B";char myC=(char)"C";char myD=(char)"D";char myF=(char)"F";
        if (numeric_grade >= 93 && numeric_grade <= 100) {
          letter_grade = myA;
        } 
        else if (numeric_grade >= 85 && numeric_grade <= 92) {
          letter_grade = myB;
        } 
        else if (numeric_grade >= 76 && numeric_grade <= 84) {
          letter_grade = myC;
        } 
        else if (numeric_grade >= 65 && numeric_grade <= 75) {
          letter_grade = myD;
        } 
        else if (numeric_grade >= 0 && numeric_grade <= 64) {
          letter_grade = myF;
        } 
        else {
          throw new IllegalArgumentException();
        }
        grade.put(Character.toString(letter_grade), numeric_grade);
        return grade;
      }
      return toLetterGrade(params.numeric_grade);
    """,
    "params": {
      "numeric_grade": 99
    }
  }
}

Hi @RainTown,

Thanks for the response and example, appreciate it.

Yeah, you're right, the docs don't say "AA" should be a char. I just assumed that since I was using single quotes and only one character, the compiler would interpret it as a char. It feels redundant that even when the variable is clearly declared as a char, you still have to cast a one-character string to it. Haven’t really seen this kind of strictness in other languages I’ve worked with, so I was just curious why it behaves that way here. So yeah, your example works, but it still doesn’t fully answer why it has to be that way in Painless.