Skip to content

Latest commit

 

History

History
167 lines (147 loc) · 4.9 KB

README.md

File metadata and controls

167 lines (147 loc) · 4.9 KB

Object Graphs and Structural Typing in Java

This gives Java a way to handle partial representations of data model while remaining type safe. Types are generated at compile time via annotations.

Getting started

<dependencies>
	<dependency>
		<groupId>com.github.henneberger</groupId>
		<artifactId>typekin-processor</artifactId>
		<version>1.0.2</version>
		<scope>provided</scope>
	</dependency>
</dependencies>

Example Usage

public class Main {
  // The optional model definition. If present, all TypeOf
  //  annotations must match this structure.
  //
  // You add `implements St{name}Model`
  @Model
  abstract class Foo implements StFooModel {
    public abstract String getA();
    public abstract String getB();
    public abstract String getC();
  }

  //A subset of the model (for code reuse)
  @TypeOf(model = Foo.class)
  public interface FooAFragment {
    String getA();
  }
  @TypeOf(model = Foo.class)
  public interface FooABFragment {
    String getA();
    String getB();
  }
  @TypeOf(model = Foo.class)
  public interface FooCFragment {
    String getC();
  }
  //compile error, D is not a property of the model
  //@TypeOf(clazz = Foo.class)
  //public interface FooDFragment {
  //  String getD();
  //}

  // A container of data that can contain any data.
  //  Function signatures are used to determine
  //  equivalence classes with TypeOf classes.
  //
  // You add `implements St{name}`
  @StructuralType(model = Foo.class)
  public static class FooAData implements StFooAData {
    public String getA() { return "A"; }
  }
  @StructuralType(model = Foo.class)
  public static class FooABData implements StFooABData {
    public String getA() { return "A"; }
    public String getB() { return "B"; }
  }
  @StructuralType(model = Foo.class)
  public static class FooCData implements StFooCData {
    public String getC() { return "C"; }
  }

  public static void main(String[] args) {
    print(new FooAData());
    print(new FooABData());
    //print(new FooCData()); //compile error
  }

  public static void print(FooAFragment foo) {
    System.out.println(foo.getA());
    //System.out.println(foo.getB()); //compile error
    //System.out.println(foo.getC()); //compile error
  }
}

//This gets generated by the annotations:
interface StFooAData extends FooAFragment {}
interface StFooABData extends FooAFragment, FooABFragment {}
interface StFooCData extends FooCFragment {}

interface StFooModel extends FooAFragment, FooABFragment, FooCFragment {}
interface StFooRef extends StFooAData, StFooABData, StFooCData {}

@Model

The @Model annotation defines the data model. All @TypeOf model fragments that refer to a @Model class will be strongy typed. Only abstract methods will be recognized for type candidates. For nested objects, a generated Ref class serves as a type reference. Parameters:

  • name: The name of the class it will generate
  • refName: The name of the class it will generate for data model relationships
  • concreteName: The name of the empty concrete class for jvm type validation

E.g.

@Model(name = "FooModel", refName = "FooRef")
abstract class Foo implements FooModel {
  public abstract String getA();
  public abstract List<BarRef> getBar();
}

@Model(name = "BarModel", refName = "BarRef")
abstract class Bar implements BarModel {
  public abstract String getA();
  public abstract FooRef getFoo();
}

@TypeOf

The @TypeOf annotation defines a partial representation of the data model. All @StructuralType classes that have the same model class are compared structurally to all @TypeOf interfaces. Parameters:

  • model: The class of the model

E.g:

@TypeOf(model = Foo.class)
public interface FooFragment {
  String getA();
  List<? extends BarFragment> getBar();
}
@TypeOf(model = Bar.class)
public interface BarFragment {
  String getA();
  FooFragment getFoo();
}

@StructuralType

A concrete type that contains data. This can contain any data but only method signatures that match the model will be used as candidates for @TypeOf classes. Parameters:

  • model: The class of the model
  • name: The name of the class it will generate

E.g.

@StructuralType(model = Foo.class, name = "FooDataType")
public class FooData extends FooDataType {
  public String getA() { return null; }
  public List<BarData> getBar() { return null; }
  public String extraParam() { return null; } //ok
}
@StructuralType(model = Bar.class, name = "BarDataType")
public class BarData extends BarDataType {
  public String getA() { return null; }
  public Foo getFoo() { return null; }
}

A full example can be found here: tests/src/main/java/com/github/henneberger/typekin/tests/example/FooExample.java

Limitations

Since it relies on compile time annotations, other annotations may not work with Typekin.

Contributions

This work is inspiried by https://github.com/tlamr/stjava