Je ne comprends pas pourquoi cela ne compile pas:
abstract class Animal {}
class Duck extends Animal {}
object Main {
def fooBar[T <: Animal ]():T = {
return new Duck()
}
}
Pourquoi j'obtiens l'erreur de compilation qui:
Obligatoire: T
Trouvé: canard
T <: animal> ne signifie-t-il pas que j'ai juste besoin de renvoyer un sous-type de Animal ? & Duck est un sous-type? C'est quelque chose que j'ai eu beaucoup de mal à comprendre. Y a-t-il un moyen de marquer dans une signature de méthode, elle devrait renvoyer un sous-type d'un type particulier?
3 Réponses :
Vous voulez faire quelque chose comme ceci:
abstract class Animal {}
class Duck extends Animal {}
object Main extends App{
def fooBar[T <: Animal](animal:T): T = {
animal
}
println(fooBar[Animal](new Duck))
println(fooBar[Duck](new Duck))
}
Ce n'est cependant pas très utile. Pourrait simplement appeler identity à la place.
D'accord, ce n'est pas du tout utile, mais je pense qu'il voulait le faire compiler pour faire maintenant la logique qu'il voulait
Pouvez-vous expliquer votre problème?
La signature actuelle def fooBar [T <: Animal] (): T dit en gros
"quel que soit le type d'animal qu'un utilisateur demande, je peux créer un animal exactement de ce type".
Cette fonction ne peut pas avoir d'implémentations raisonnables.
Il n'y a littéralement aucun problème, simplement essayer de comprendre les limites de type. C'est juste quelque chose que j'ai intuitivement senti que je serais capable d'exprimer, que cela ait ou non de la valeur ou non, ne sonne probablement pas comme cela sur la base de ces réponses, mais je pensais néanmoins que cela pourrait être possible.
Une règle de base: vous n’avez jamais besoin de limites de type <: sauf si vous faites des choses informes. 99% des cas sont couverts par un polymorphisme paramétrique normal ( fooBar [T] ), oldschool OOP ( fooBar (): Animal ) ou un polymorphisme restreint aux classes de types ( fooBar [T: Animal] (): T ).
@simpadjo Je ne suis pas d'accord, j'ai trouvé <: très utile Je l'utilise beaucoup dans mon code et je n'ai jamais utilisé directement Shapeless , et la plupart de mon code est écrit sur un < style b> FP utilisant des classes de types pour la plupart de mes choses. J'ai également trouvé la <: < contrainte de type généralisée très utile.
T <: Animalne signifie-t-il pas que je dois simplement renvoyer un sous-type deAnimal?
Non. Cela signifie que vous devez renvoyer un T , choisi par l'appelant de votre méthode. L'appelant est contraint d'en faire un sous-type de Animal , mais il peut s'agir d'un Canard , d'un Chien ou d'un Cat code>.
Vous ne pouvez pas simplement renvoyer un Duck , car l'appelant peut avoir voulu un Cat .
Sans un paramètre supplémentaire pour vous dire ce qui doit être retourné, la seule façon de l'implémenter serait de retourner Rien , c'est-à-dire de lancer une exception, de renvoyer null (mais ne faites pas ça dans Scala) ou ne revenez jamais du tout.
J'ai juste besoin de renvoyer un sous-type de
Animal
Si vous le souhaitez, le type de retour doit être déclaré comme Animal .
Vous pouvez toujours renvoyer un Canard lorsqu'un Animal est requis.
Pourquoi je ne peux pas spécifier que vous pouvez renvoyer un sous-type d'Animal mais pas un Animal lui-même!
Il n'y a aucun moyen de spécifier cela dans le système de types. Et cela ne devrait pas non plus être vraiment nécessaire. Si vous dites que vous avez besoin d'un Animal , tout ce qui implémente l'interface définie par celui-ci est censé être assez bon.
"Solutions de contournement" possibles:
Animal une classe abstraite . Ensuite, il ne peut y avoir aucune instance de Animal lui-même, et toutes les instances seront des instances de sous-classe. trait et déclarez le type comme ActualAnimal ou Animal with ActualImplementation . Mais ce serait un travail supplémentaire de votre part d'ajouter ce trait là où c'est nécessaire (et assurez-vous de ne pas l'ajouter à Animal ) Et si Animal n'était pas abstrait et que je veux que quiconque remplace une méthode pour renvoyer un sous-type d'Animal, parce que s'ils retournaient juste un animal, disons que l'univers prendrait fin (; donc je ne peux pas permettre à leur code de se compiler ! Ha non c'est vraiment juste pour ma propre compréhension pourquoi je ne peux pas spécifier que vous pouvez renvoyer un sous-type d'animal mais pas un animal lui-même!? Je sais qu'un sous-type d'animal est un animal mais un animal lui-même est simplement un animal, pas un sous-type d'Animal, ou est-ce incorrect d'une manière ou d'une autre?
def fooBar (): Animal = new Duck ()ne fonctionne pas pour vous?Pouvez-vous expliquer quel est votre cas d'utilisation? Je pense que les réponses déjà fournies font un excellent travail pour expliquer pourquoi cela ne fonctionne pas. Mais j'ai le sentiment qu'ils ne vous suffisent pas, car ils ne résolvent probablement pas votre vrai problème. Cependant, pour être en mesure de répondre à cela, nous aurions besoin de le savoir.
Ouais, je n’ai pas vraiment de cas d’utilisation, je m'amuse juste à jouer avec Scala, à faire des trucs dingues et à échouer haha. Je pensais juste qu'il y aurait quelque chose comme ça pour fournir plus d'informations au compilateur Scala et voir ce qu'il fera. Vous savez quand vous ajoutez une liste de chiens et une liste de chats vous récupérez une liste d'animaux? Probablement pas lié à cela, mais juste à l'exploration.
@ Snickers3192 Vous devriez taguer quelqu'un en répondant lol, vous avez eu de la chance que je viens de décider de revérifier cela. Quoi qu'il en soit, comme indiqué dans les commentaires, ce que vous voulez réaliser n'est pas possible dans le cas général. Vous avez généralement quatre cas: 1. Vous voulez pouvoir changer l'objet retourné sans affecter le code utilisateur, c'est le principe du sous-typage, vous promettez simplement de retourner un super type et vous pouvez renvoyer n'importe quel sous-type de celui-ci. 2. Vous voulez que le code se comporte différemment selon le type demandé par l'utilisateur, vous pouvez résoudre cela avec une typeclass .
3. Vous voulez retourner ce type spécifique, comme tous les animaux ont une méthode
renommerqui renvoie une copie d'eux avec le nom changé, donc si j'ai unChienje veux un autreChien, vous pouvez y parvenir en utilisant le polymorphisme F-Bounded , mais généralement une classe de types est une meilleure approche. 4. Vous voulez une sortie différente avec une entrée statique commedef getAnimalByIndex [A <: Animal] (idx: Int): T = if (idx == 0) Dog else Cat, et cela ne peut que être appelé oùidxest une constante de temps de compilation, donc si j'appellegetAnimalByIndex (0)je m'attends à ce que le résultat soit de typeDog, ceci sera possible dans Scala 3 .(Je pense que vous pouvez réaliser ce quatrième cas dans Scala 2 en utilisant des macros , mais je n’ai aucune expérience avec cela) .