Java Semantic Errors | Why Java Programmer Must Acknowledge Semantic Errors

Mouad Oumous
The Fresh Writes
Published in
10 min readMar 7, 2023

Join JAVA FLAMES

Many people find the difference between semantic errors and syntactical (syntax) errors in Java code hard to understand, but they are different. You can see a semantic error when the syntax of your code is correct but the code usage isn’t correct.

· What is a semantic error?
· How semantic errors are detected?
· Common Semantic Errors

What is a semantic error?

A semantic error is a problem in your code that prevents the interpreter from understanding it. There may not be anything wrong with the logic you’ve written, but it will cause the program to crash the way you’ve written. For instance, the following code will produce an incorrect result in a Semantic runtime Error:

var a = 1 // No error
var b = 2 // No error
var c = 3 // No error
a + c = b

A semantic error is a language-level error in a program, typically when you try to use an identifier that has not been previously defined.

Semantic errors are detected at compile time, and you can think of them as static analysis errors. For example, if you try to execute a variable that you have not declared, it will throw a semantic error: A semantic error occurs when a programmer writes code that fails to communicate its intended purpose. This is different from a syntax error, which occurs when the code is syntactically correct but does not perform its designed task.

In other words, a semantic error refers to an incorrect program that produces a result that doesn’t make sense. For example, if a weather app said “Warm and sunny” even when outside it was raining, this would be a semantic error. The program did not produce what it was supposed to produce.

How semantic errors are detected?

some of the semantic errors (the static semantic errors) are detected by the compiler, which generates a message indicating the type of error and the position in the Java source file where the error occurred (notice that the actual error could have occurred before the position signaled by the compiler).

At this point, we have described static semantic errors. In each case, the compiler flags a static semantic error when a requirement of the programming language is not satisfied (invalid types).

There is another type of problem: what happens if your program compiles and executes, but does not do what you wanted it to do? The computer is doing exactly what you programmed, but what you programmed was wrong. This is another type of semantic error: when the programmer incorrectly uses the programming language to solve the problem.

Unlike a static semantic error, a dynamic semantic error has to do with meaning. If a program contains this kind of error, it will successfully run, but won’t output the correct result.

Debugging is not that easy with a semantic error. Let’s see an example.

/**
* This program prints the total benefit for the company
*
*/
public class TotalBenefit {
public static void main(String[] args) {
int branch1 = 250;
int branch2 = 500;
System.out.println("The total benefit is $" + (branch1 * branch2));
}
}

This program will output The total benefit is $125000. This is incorrect; the expected output is 750 . This happens because we use a multiply ( * ) sign instead of an addition ( + ) sign.

Common Semantic Errors

Incompatible types error

This syntax error is very common in beginners. In compiled programming languages like Java, you can only assign values to compatible variables.

Compatible means that they have the same type or, they have types that can be automatically converted by the compiler.

In the example below you can see that in Java, you cannot assign a double to an integer variable. Some programming languages might do the conversion automatically, but if you see the error below, the fix is simple. Just change the type of the variable ‘a’, or assign an integer value instead of a double one (5.5).

This type of semantic error can be tricky because the compiler will flag some errors and not others. For example, if you try to assign a float to an int variable, the compiler displays an error message.

On the other hand, if you assign an int to a float variable, the compiler performs an automatic type conversion to the int to make it a float. The problem with this second scenario is that it can silently introduce errors in your code, especially if you really did mean to use a float.

“variable <X> might not have been initialized”

This occurs when a local variable declared within a method has not been initialized. It can occur when a variable without an initial value is part of an if statement.

int x;
if (condition) {
x = 5;
}
System.out.println(x); // x may not have been initialized

Performing an impossible cast

It’s possible to convert between many different types in Java. However, no matter how much you’d like to convert a boolean value into an int, Java won’t let you do it. The concept of performing the cast is syntactically correct, but you’re applying it incorrectly, making this a semantic error that the compiler always catches.

The “inconvertible types” error occurs when the Java code tries to perform an illegal conversion.

TypeInvocationConversionTest.java:12: inconvertible types
found : java.util.ArrayList<java.lang.Class<? extends TypeInvocationConversionTest.Interface1>>
required: java.util.ArrayList<java.lang.Class<?>>
lessRestrictiveClassList = (ArrayList<Class<?>>) classList;
^

For example, booleans cannot be converted to an integer.

“non-static variable . . . cannot be referenced from a static context”

This error occurs when the compiler tries to access non-static variables from a static method

public class StaticTest {
private int count=0;
public static void main(String args[]) throws IOException {
count++; //compiler error: non-static variable count cannot be referenced from a static context
}
}

To fix the “non-static variable . . . cannot be referenced from a static context” error, try these two things:

  • Declare the variable as static in the signature.
  • Check on the code as it can create an instance of a non-static object in the static method.

“non-static method . . . cannot be referenced from a static context”

This issue occurs when the Java code tries to call a non-static method in a non-static class. Here is an example:

class Sample {
private int age;
public void setAge(int a) {
age=a;
}
public int getAge() {
return age;
}
public static void main(String args[]) {
System.out.println(“Age is:”+ getAge());
}
}

Would return this error:

Exception in thread “main” java.lang.Error: Unresolved compilation problem:
Cannot make a static reference to the non–static method getAge() from the type Sample

To call a non-static method from a static method is to declare an instance of the class calling the non-static method.

You’ll get the “(array) <X> not initialized” message when an array has been declared but not initialized. Arrays are fixed in length so each array needs to be initialized with the desired length.

The following code is acceptable:

AClass[] array = {object1, object2}
As is:
AClass[] array = new AClass[2];

array[0] = object1;
array[1] = object2;
But not:
AClass[] array;

array = {object1, object2};

“Operator .. cannot be applied to <X>”

This issue occurs when operators are used for types, not in their definition.

Operators are only defined for certain types, although implicit type conversion is allowed between certain numeric types:

int a = 5; 
boolean b = true;
int c = a + b; // Error, can't add a boolean value double
d = a + 1.4; // OK, int is implicitly converted to double

The operator + is undefined for the argument type(s) int, boolean

This often happens when the Java code tries to use a type string in a calculation. To fix it, the string needs to be converted to an integer or float.

Possible loss of precision

Sometimes you can apply casting incorrectly. For example, casting a float to an int works fine, but at the loss of the decimal portion of the number.

“Possible loss of precision” occurs when more information is assigned to a variable than it can hold. If this happens, pieces will be thrown out. If this is fine, then the code needs to explicitly declare the variable as a new type.

This error arises when trying to assign a value of higher precision to a variable of lower precision without anexplicit type cast. Surprisingly, perhaps, floating point literals are of type double and you will get this message if you try to assign one to a variable of type float :

float sum = 0.0;  // Error, literal is not of type float

The correct way to do this is to use a literal of type float or an explicit type cast:

float sum = 0.0f;   // OK float sum = (float) 0.0;  // OK

Type mismatch: cannot convert from double to float

This loss of precision can affect the output of your application in unexpected ways and cause the output to reflect a value other than the one you expected. The compiler never finds this sort of error because you have specifically applied a cast to the variable and the compiler expects that you know what you’re doing.

Applying scoping incorrectly

Any variable you declare inside a method has the same scope — visibility to other parts of the application, in other words — as the method. Consequently, you can’t declare a private static int variable inside a method. Instead, you must define the variable globally like this:

public class PrivateVar
{
// This declaration works.
private static int PrivateInt = 3;
public static void main(String[] args)
{
// This declaration doesn’t work.
private static int PrivateInt = 3;
}
}

Other Common Semantic Errors

Here are some other general cases of semantic errors in Java

Type mismatches — There are a lot of ways to use the wrong type in C#. The example above is one way. Here are some other possible ways:

  • Passing the wrong type of value to a method.
  • Returning the wrong type of value from a method.
  • Setting the wrong type of value on a property.
  • Setting the wrong type of value on a field.
  • Setting the wrong type of value on a local variable.
  • Using a type in a generic argument list which doesn’t conform to the generic type constraints.

Arity mismatch — This means using the wrong number of arguments for something and there are many ways to do this as well:

  • Passing the wrong number of arguments to a method call.
  • Passing the wrong number of indexes to an array or indexer.
  • Using the wrong number of generic type arguments for a generic type or method.
  • Using undefined names — Using a namespace, type, method, field, variable, or property name which doesn’t exist.

Naming collisions — Here are some ways names can collide:

  • Defining two types in the same namespace with the same name
  • Defining two methods in the same class with the same argument list and name.
  • Defining a property, field, or nested type which has the same name as another property, field, or nested type in the same owning type.
  • Defining two local variables in the same or descendant scopes with the same names.

Thanks for reading.Happy learning 😄

Do support our publication by following it

--

--

Mouad Oumous
The Fresh Writes

I'm a blogger on medium, l'm a java and kotlin developer, also an Android Developer I mastered various topics such as Networking, UI , UX, Animation ...