2017-04-14 4 views
1

Je l'intégration avec une api java, qui ressemble un peu comme ça (ce sont des classes java, je vais juste utiliser la syntaxe scala par souci de concision:paramètres type Inférer

class AbstractFooBuilder[ActualType <: AbstractFooBuilder, WhatToBuild <: Foo] { 
    // ... 
    def withFoo(f: Foo): ActualType 
    def withBar(b: Bar): ActualType 
    // ... 
    def build: WhatToBuild   
} 

class FooBarBuilder extends AbstractFooBuilder[FooBarBuilder, FooBar] 
class FooBazBuilder extends AbstractFooBuilder[FooBazBuilder, FooBaz] 
// .. etc 

Il y a un tas de ceux-ci, et Je suis en train de faire de la création de ces foos moins répétitif avec quelque chose comme ceci:

def anyFoo[T <: Foo, B <: AbstractFooBuilder[B, T] : Manifest](foo: Foo, b: Bar) = manifest 
    .runtimeClass 
    .newInstance 
    .withFoo(foo) 
    .withBar(bar) 
    .build 

le problème est que maintenant, pour créer FooBar, je dois écrire quelque chose comme ceci:

val foobar = new anyFoo[FooBar, FooBarBuilder](foo, bar) 

qui est plus long que je le voudrais. Plus précisément, une fois FooBarBuilder paramètre de type est connu, FooBar est la seule possibilité pour la deuxième ... Je me demande param s'il y a un truc qui me manque, qui permettrait de « déduire » l'autre param et ne doivent préciser un.

Des idées?

Répondre

1

Malheureusement, l'astuce standard pour diviser les paramètres de type ne fonctionne pas ici parce que B dépend T.

Mais qu'en se débarrassant de Manifest et en simplifiant à

def anyFoo[T <: Foo](builder: AbstractFooBuilder[_, T])(foo: Foo, b: Bar) = 
    builder.withBar(b).withFoo(foo).build 

? Vous l'appelez anyFoo(new FooBarBuilder)(foo, b), donc T est inféré, résolvant votre problème. Et en prime, c'est plus rapide et il n'y a pas d'erreur d'exécution si votre classe ne dispose pas d'un constructeur par défaut.

Si votre méthode n'a pas toujours besoin de créer le constructeur, vous pouvez utiliser le paramètre ou () => AbstractFooBuilder[_, T] par nom.

EDIT: Compte tenu du commentaire, cela pourrait fonctionner:

def mkBuilder[B <: AbstractFooBuilder[B, _] : Manifest]: B = // the complex creation code 

anyFoo(mkBuilder[FooBarBuilder])(foo, b) // infers FooBar 

La question est de savoir si vous pouvez mettre en œuvre mkBuilder sans accès à T, mais si votre code d'origine n'a pas besoin de Manifest[T], il devrait être possible .

Ou encore

implicit class AnyFoo[T, B <: AbstractFooBuilder[B, T]](builder: B) { 
    def apply(foo: Foo, b: Bar) = builder.withBar(b).withFoo(foo).build 
} 

mkBuilder[FooBarBuilder](foo, b) 
+0

Ouais, c'est ce que je fini par faire ... Il est regrettable, parce que la création du constructeur lui-même est assez fastidieux dans la réalité, pas seulement 'nouveau Builder' comme je l'ai simplifié dans la question, et je voulais l'appelant de pouvoir éviter d'avoir à le faire partout. Oh bien ... – Dima

+0

Voir la retouche. Cela résout-il votre problème? –

+0

Ouais, ça marche ...bien que, pas beaucoup plus court, que 'anyFoo [Foo, FooBarBuilder] (foo, b)', mais pas aussi laid :) – Dima

0

Si je lis correctement le code. Le code n'implique pas qu'il n'y a qu'une seule usine AbstractFooBuilder pour ce type et il est laissé à l'appelant pour spécifier l'usine à utiliser.

Si vous pouvez utiliser implicite, vous pouvez faire quelque chose comme ça

def anyFoo[T <: Foo](foo: Foo, b: Bar)(implicit factory: AbstractFooBuilder[T]): T = { 
     factory. 
      withBar(b). 
      withFoo(foo). 
      build 
} 
+0

Oui, l'appelant doit spécifier l'usine, mais il se sent, comme il devrait être possible d'éviter _also_ devoir spécifier le type de l'objet, qu'il construit, parce que c'est déterminé par le type d'usine. – Dima