javagenericsjavafxinterfacebounded-types

Why no compile error at CustomerService service = ServiceFactory.getInstance().getServiceType(ServiceType.CUSTOMER);


SuperService:

package service;

public interface SuperService {
}

CustomerService:

package service.custom;

import dto.Customer;

import java.util.List;

public interface CustomerService{
    boolean add(Customer customer);
    Customer search(String id);
    boolean update(String id, Customer customer);
    boolean delete(String id);
    List<Customer> getAll();
}

CustomerServiceImpl:

package service.custom.impl;

import dto.Customer;


public class CustomerServiceImpl{
    public boolean add(Customer customer) {
        return false;
    }
}

ServiceFactory:

package service;

import service.custom.impl.CustomerServiceImpl;
import service.custom.impl.ItemServiceImpl;
import service.custom.impl.OrderServiceImpl;
import util.ServiceType;

public class ServiceFactory {
    private static ServiceFactory instance;

    private ServiceFactory(){

    }

    public static ServiceFactory getInstance(){
        if(instance==null){
            instance= new ServiceFactory();
        }
        return instance;
    }

    public <T extends SuperService> T getServiceType(ServiceType type){
        switch (type){
            case CUSTOMER:return (T) new CustomerServiceImpl();
            case ITEM:return (T) new ItemServiceImpl();
            case ORDER:return (T) new OrderServiceImpl();
        }
        return null;
    }
}

CustomerFormController:

package controller;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import dto.Customer;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import service.ServiceFactory;
import service.custom.CustomerService;
import util.ServiceType;

public class CustomerFormController {

    @FXML
    private JFXButton btnAdd;

    @FXML
    private JFXButton btnDelete;

    @FXML
    private JFXButton btnReload;

    @FXML
    private JFXButton btnSearch;

    @FXML
    private JFXButton btnUpdate;

    @FXML
    private TableColumn<?, ?> colAddress;

    @FXML
    private TableColumn<?, ?> colId;

    @FXML
    private TableColumn<?, ?> colName;

    @FXML
    private TableColumn<?, ?> colSalary;

    @FXML
    private TableView<?> tblCustomers;

    @FXML
    private JFXTextField txtAddress;

    @FXML
    private JFXTextField txtId;

    @FXML
    private JFXTextField txtName;

    @FXML
    private JFXTextField txtSalary;



    @FXML
    void btnAddOnAction(ActionEvent event) {
        String idText = txtId.getText();
        String nameText = txtName.getText();
        String addressText = txtAddress.getText();
        double salary = Double.parseDouble(txtSalary.getText());

        Customer customer = new Customer(idText, nameText, addressText, salary);
        CustomerService service = ServiceFactory.getInstance().getServiceType(ServiceType.CUSTOMER);
        service.add(customer);
    }

    @FXML
    void btnDeleteOnAction(ActionEvent event) {

    }

    @FXML
    void btnReloadOnAction(ActionEvent event) {

    }

    @FXML
    void btnSearchOnAction(ActionEvent event) {

    }

    @FXML
    void btnUpdateOnAction(ActionEvent event) {

    }
}

This is a layered architecture design and here I have tried to use the factory design pattern to pass a CustomerServiceImpl object from the service layer to the controller/presentation layer. I was trying to understand bounded type generics and decided to do an experiment to understand the concept then only i ran into the problem. Can you help me understand why there is no compile error at CustomerService service = ServiceFactory.getInstance().getServiceType(ServiceType.CUSTOMER); since the CustomerService doesn't extend SuperService and CustomerServiceImpl doesn't implement CustomerService so shouldn't the compile understand that a CustomerService reference cannot be used for a CustomerServiceImpl object?


Solution

  • You don´t get a Compile Error because you are casting here:

    public <T extends SuperService> T getServiceType(ServiceType type){
        switch (type){
            case CUSTOMER:return (T) new CustomerServiceImpl();
            case ITEM:return (T) new ItemServiceImpl();
            case ORDER:return (T) new OrderServiceImpl();
        }
        return null;
    }
    

    With type erasure in place you basically casting all the Impls to SuperService.

    Now since SuperService is an interface you won´t get a compile error, but you will get a runtime error!

    It basically boils down to this:

    Casting to unrelated interfaces does not give you a compile error.

    More info can be found here: Why does it compile when casting to an unrelated interface?