Search code examples
javajavafxcompiler-warnings

JavaFX Compilation Warning - "uses unchecked or unsafe operations" - Raw Data Types?


I am learning JavaFX, specifically trying to implement a table using the TableColumn and TableView classes. My program compiles without error, but I get an ominous-looking warning at compilation time:

C:\JavaFX_Sandbox>javac robotApp.java
Note: robotApp.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

C:\JavaFX_Sandbox>

This sufficiently weirded me out enough that I haven't had the nerve to try actually running my program. I did some digging here on StackOverflow plus a number of other sites, and discover that the most likely cause of this warning is that I'm using raw data types. But if so, I can't spot them. And if not, I'm worried what this message could mean.

To describe my program as quickly as possible: I've created a nerdy robot.java class:

public class robot{

    private String name;
    private int intelligence;

    public robot(String a, int b){
        this.name = a;
        this.intelligence = b;
    }
}

Nothing fancy. To store all those robots, I created a robotMgr.java class, essentially a glorified ArrayList to hold them all:

import java.util.ArrayList;
import java.util.List;
import javafx.collections.*;

public class robotMgr{

    private ArrayList<robot> robotList;
    private ObservableList<robot> oRobotList;

    robotMgr(){
        robotList = new ArrayList<robot>();
        robotList.add(new robot("R2-D2", 7));
        robotList.add(new robot("Cylon", 3));
        robotList.add(new robot("The Terminator", 5));
        robotList.add(new robot("WALL-E", 6));
        populateOList();
    }
    public void populateOList(){
        for(int i=0; i<robotList.size(); i++)
            oRobotList.add(robotList.get(i));
    }

    public List<robot> getRobotList(){
        return robotList;
    }
    public ObservableList<robot> getORobotList(){
        return oRobotList;
    }
}

(I do more data manipulation in the ArrayList, but the ObservableList is necessary because the TableColumn class expects to be fed the table elements from an ObservableList.)

Now for the Main Event: A JavaFX program which takes the data above and displays it in a simple, two-column table:

import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.event.*;
import javafx.geometry.*;
import javafx.collections.*;

public class robotApp extends Application{

    public static void main(String[] args){
        launch(args);   
    }

    TableColumn<robot, String> colName;
    TableColumn<robot, Integer> colIntelligenge;
    TableView<robot> robotTable;

    BorderPane borderPane;
    Scene scene;
    Stage stage;

   @Override public void start(Stage primaryStage){

        robotMgr myBots = new robotMgr();

        colName = new TableColumn<robot, String>("Name");
        colName.setMinWidth(100);
        colName.setCellValueFactory(new PropertyValueFactory<robot, String>("Name"));
        colIntelligenge = new TableColumn<robot, Integer>("Intelligence");
        colIntelligenge.setMinWidth(100);
        colIntelligenge.setCellValueFactory(new PropertyValueFactory<robot, Integer>("Intelligence"));

        robotTable = new TableView<robot>();
        robotTable.getColumns().addAll(colName, colIntelligenge);
        robotTable.setItems(myBots.getORobotList());

        borderPane = new BorderPane();
        borderPane.setCenter(robotTable);
        scene = new Scene(borderPane, 600, 600);
        stage = primaryStage;
        stage.setScene(scene);
        stage.setTitle("Robot App");
        stage.show();

    }
}

All looks good to me. And everything compiles fine, except for the warning messages I mentioned above. The weird thing is, the message goes away if I comment out the "robotTable.getColumns().addAll(colName, colIntelligenge);" line only.

So... I'm completely baffled. If the error message is warning me about using raw data types, I don't know where they are in my program. And if the error message is warning about something else, I don't know what that might be. Also: why would the addAll() method be the line that causes the warning? I can't figure that one out for the life for me.

Is the issue that the robots' instance variables are all private? If so, wouldn't it create a specific syntax error at compilation time? Or is the ObservableList the problem? In understand that OLs are interfaces, not data structures... but I don't see how this could trip me up here.

If anyone can offer some help or advice, I will be incredibly grateful.

Many thanks! -RAO


Solution

  • Your code is perfectly safe to run (it has bugs, which you'll discover when you run it, but those are unrelated to the typesafety about which you're asking).

    Basically, the issue is that the addAll() method you are invoking takes a "varargs" parameter whose type is a generic type. For a TableView<S>, TableView.getColumns() returns an ObservableList<TableColumn<S,?>> and the ObservableList<E> addAll() method you are then calling takes a vararg parameter: addAll(E... elements). So you end up calling a method with signature addAll(TableColumn<Robot, ?>... columns).

    Varargs are processed by converting them to an array, so you implicitly create an array of type TableColumn<Robot, ?>[] when you call the addAll method. Because arrays and parameterized types do not play well together, the compiler is unable to ensure the type safety of this array. Consequently, it issues a warning. See, e.g. here for an example of why the compiler can't ensure type safety here.

    The authors of the addAll() method probably should have coded it so that it ensured type safety and annotated it so that it avoided this warning (if that's possible: I'd need to think about it for more time than I have right now).

    To avoid the warning, you can simply invoke the add method twice:

    robotTable.getColumns().add(colName);
    robotTable.getColumns().add(colIntelligenge);
    

    Alternatively you can create a List of table columns and pass that to the (other) addAll(...) method that takes a collection, instead of a varargs parameter:

    robotTable.getColumns().addAll(Arrays.asList(colName, colIntelligence));