Skip to content

Problematic use of generic types #238

Closed

Description

@ldcasillas-progreso

Using TestContainers 1.1.6, the following code produces a compile time error:

private static final PostgreSQLContainer postgres =
        new PostgreSQLContainer("postgres:9.5.4")
                .withDatabaseName("test")
                .withEnv("PGDATA", CONTAINER_PGDATA)
                .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

Compilation error:

/Users/my-user-name/my-project/my/path/to/IntegrationTest.java:71: error: incompatible types: GenericContainer cannot be converted to PostgreSQLContainer
                    .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE)

The problem stems from the fact that the PostgreSQLContainer class starts like this, with a recursive bound on its generic type parameter (à la class Enum<E extends Enum<E>>):

public class PostgreSQLContainer<SELF extends PostgreSQLContainer<SELF>> 
        extends JdbcDatabaseContainer<SELF>

So this is a generic class, but the documentation and example test case use it with no type parameter, as a raw type:

@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();

And in this situation Java can't figure out that in my use of the withFileSytemBind() method, the SELF return type unifies with PostgreSQLContainer.

My workaround for this issue illustrates the fix:

// A dummy class whose sole purpose is to instantiate the `SELF`
// type parameter.
private static final class MyPostgreSQLContainer extends PostgreSQLContainer<MyPostgreSQLContainer> {
    public MyPostgreSQLContainer(String dockerImageName) {
        super(dockerImageName);
    }
}

private static final MyPostgreSQLContainer postgres =
        new MyPostgreSQLContainer("postgres:9.5.4")
                .withDatabaseName("test")
                .withEnv("PGDATA", CONTAINER_PGDATA)
                .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

This argues that the existing "end user facing" classes like PostgreSQLContainer in the library ought to be split into two classes:

public abstract class AbstractPostgreSQLContainer<SELF extends AbstractPostgreSQLContainer<SELF>> 
        extends JdbcDatabaseContainer<SELF> {
    ...
}

public class PostgreSQLContainer extends AbstractPostgreSQLContainer<PostgreSQLContainer> {
    ...
}

People who want to extend PostgreSQLContainer should be discouraged from doing so as well, and pointed to AbstractPostgreSQLContainer as the proper class for extension. (Making PostgreSQLContainer a final class is one alternative here, but with the downside that it's a backwards-incompatible change.)

Activity

  1. bsideup commented on Oct 26, 2016

    @bsideup
    Member

    @ldcasillas-progreso thanks for the report!

    As a temporary workaround, I can suggest this:

    private static final PostgreSQLContainer postgres =
       new PostgreSQLContainer<>("postgres:9.5.4")
        .withDatabaseName("test")
        .withEnv("PGDATA", CONTAINER_PGDATA)
        .withFileSystemBind(HOST_PGDATA.toString(), CONTAINER_PGDATA, BindMode.READ_WRITE);

    (note the diamond operator)

  2. ldcasillas-progreso commented on Oct 26, 2016

    @ldcasillas-progreso
    Author

    @bsideup Ah, good one!

  3. rnorth commented on Nov 20, 2016

    @rnorth
    Member

    @ldcasillas-progreso yes, I can see how this would be annoying, and we're keen to come up with a more user-friendly model (which will probably be a breaking change and the focus of a future 2.0.0 release).

    In the meantime @bsideup's workaround is the right way, and I'd like to think about your suggested fix too. It looks pretty sensible to me. It might be a good solution for users on the 1.x.x series and something we can integrate reasonably soon.

  4. modified the milestone: 2.0 on Jan 14, 2017
  5. mpecan commented on Jul 18, 2017

    @mpecan

    This issue also makes it very hard to use Testcontainers in Kotlin (and still exists in the current 1.2.1 version).

    Is there any progress on this? Can I help out with a PR?

  6. bsideup commented on Jul 18, 2017

    @bsideup
    Member

    Hi @mpecan,

    If you have an idea how to solve self-referencing without breaking current behavior it would be great!

    You can also submit a bug to Kotlin... Well... Because it's a bug in Kotlin and a valid JVM bytecode :D

  7. martin-g commented on Jul 18, 2017

    @martin-g

    The solution I've shared at #318 (comment) works without any issues for me.

  8. mpecan commented on Jul 18, 2017

    @mpecan

    @martin-g Agreed. And I can see why.

    @bsideup I will give it a stab in the next couple of days.

  9. stale commented on Oct 28, 2018

    @stale

    This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you believe this is a mistake, please reply to this comment to keep it open. If there isn't one already, a PR to fix or at least reproduce the problem in a test case will always help us get back on track to tackle this.

  10. stale commented on Nov 11, 2018

    @stale

    This issue has been automatically closed due to inactivity. We apologise if this is still an active problem for you, and would ask you to re-open the issue if this is the case.

  11. martinandersson commented on Apr 26, 2021

    @martinandersson

    Reopen please. Should be an absolute top priority if you ask me.

  12. missingfaktor commented on Dec 21, 2021

    @missingfaktor

    Could you please reopen this issue? I'm trying to use this library in Scala, and with the way generics are currently used here, it's really difficult. I'm happy to help out with PRs.

  13. mikelhamer commented on Feb 19, 2022

    @mikelhamer

    Is this "2.0" I see mentioned in this issue and other related issues about generic types actually happening? Even though it seems like a minor thing, I can't stand the thought of using something where I had to tell the IDE to suppress warnings for constantly

  14. bsideup commented on Feb 20, 2022

    @bsideup
    Member

    We're experimenting with a new "container definitions API" that will be an experimental alternative API in 1.x and get promoted as the main one in 2.0 if the feedback is good.

    You can preview it here:
    #4465

    Usage example:
    https://gist.github.com/bsideup/8247229e8a8dd86ac8bcf490e2ce6acb

  15. mikelhamer commented on Feb 20, 2022

    @mikelhamer

    Thank you I love you and I love this project. Keep up the good work everyone! Really awesome tool youve built here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions