Question

In this period I am working on a project which requires some kind of abstraction, but I don't know how to solve some problems related to java generics and type inference.

This is a simplified architecture of the source code:

public abstract class BaseDataset
{
    // Some data
    // Some methods
}

public class DerivedDataset1 extends BaseDataset
{
    // Some data
    // Implementations of some BaseDataset methods
    // Some additional methods
}

public class DerivedDataset2 extends BaseDataset
{
    // Some data
    // Implementations of some BaseDataset methods
    // Some additional methods
}

public interface BaseMeasure<T extends BaseDataset>
{
    public float evaluate(T dataset);
}

public class DerivedMeasure1 implements BaseMeasure<DerivedDataset1>
{
    @Override
    public float evaluate(DerivedDataset1 dataset)
    {
        // evaluate some measure using DerivedDataset1 methods
        return the evaluated measure;
    }
}

public class DerivedMeasure2 implements BaseMeasure<DerivedDataset2>
{
    @Override
    public float evaluate(DerivedDataset1 dataset)
    {
        // evaluate some measure using DerivedDataset2 methods
        return the evaluated measure;
    }
}

public class SocialNetworkBuilder
{
    public <T extends BaseDataset> void build(T dataset, BaseMeasure<T> measure)
    {
        float weight = measure.evaluate(dataset);

        // ...
    }
}

My problem is that if, in my main class, I write something like this:

public class Application
{
    public static void main(String [] args)
    {
        BaseDataset dataset = new DerivedDataset1();
        BaseMeasure<? extends BaseDataset> measure = new DerivedMeasure1();
        SocialNetworkBuilder socialNetworkBuilder = new SocialNetworkBuilder();
        socialNetworkBuilder.build(dataset, measure);
    }
}

Eclipse gives me the error: "The method build(T, BaseMeasure) in the type SocialNetworkBuilder is not applicable for the arguments (BaseDataset, BaseMeasure)"

I think the problem is that the "build" method has to be sure that dataset and measure are of the same type T.

If I do something like this:

public class Application
{
    public static void main(String [] args)
    {
        BaseDataset dataset = new DerivedDataset1();
        BaseMeasure<? extends BaseDataset> measure = new DerivedMeasure1();
        SocialNetworkBuilder socialNetworkBuilder = new SocialNetworkBuilder();
        socialNetworkBuilder.build((DerivedDataset1) dataset, (DerivedMeasure1) measure);
    }
}

it works, but I can't solve my problem in this way because I don't know the derived type of my "dataset" and "measure" instances at compile-time; "dataset" and "measure" should be instanced depending on some parameters given at runtime.

I need to instance my "measure" and "dataset" variables at runtime and still be able to call the "build" method on them.

Do you have some ideas to solve this problem?

Thank you.

Was it helpful?

Solution

Your application will compile when you change it as follows:

public class Application
{
    public static void main(String [] args)
    {
        DerivedDataset1 dataset = new DerivedDataset1();
        BaseMeasure<DerivedDataset1> measure = new DerivedMeasure1();
        SocialNetworkBuilder socialNetworkBuilder = new SocialNetworkBuilder();
        socialNetworkBuilder.build(dataset, measure);
    }
}

OTHER TIPS

I think that there's nothing wrong with your code. It should be correct in principle, but the type inference is not powerful enough to process the transitive type dependency between the BaseMeasure<DerivedDataset1> and DerivedDataset1 extends BaseDataset.

As an example, in Scala, your original code just works.

object DataSet  extends App {

    abstract class BaseDataset  

    class DerivedDataset1 extends BaseDataset   

    class DerivedDataset2 extends BaseDataset   

    trait BaseMeasure[T <: BaseDataset] {
        def evaluate(dataset:T):Float
    }

    class DerivedMeasure1 extends BaseMeasure[DerivedDataset1] {
        override def evaluate(dataset: DerivedDataset1) = 0
    }

    class DerivedMeasure2 extends BaseMeasure[DerivedDataset2]  {
        override def evaluate(dataset: DerivedDataset2) = 1
    }

    class SocialNetworkBuilder {
        def  build[T <: BaseDataset](dataset: T, measure:BaseMeasure[T] ) : Unit = {
            val weight = measure.evaluate(dataset);
            println(weight)
        }
    }

    val dataset1 = new DerivedDataset1()
    val measure1 = new DerivedMeasure1()
    val dataset2 = new DerivedDataset2()
    val measure2 = new DerivedMeasure2()
    val socialNetworkBuilder = new SocialNetworkBuilder()
    socialNetworkBuilder.build(dataset1, measure1)
    socialNetworkBuilder.build(dataset2, measure2)
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top