Mais uma vez, tente e tente

Exceções verificadas e não

Em suma, são necessárias exceções para separar o cenário positivo (quando tudo está indo bem) do negativo (quando ocorre um erro e o cenário positivo é interrompido). Isso é útil porque, muitas vezes, há poucas informações no código para tratar o erro e você precisa transmitir informações sobre o que aconteceu acima.





Por exemplo, existe uma função para ler um número de um arquivo (ou não um número, não importa):





String readStoredData(String id) throws FileNotFoundException, IOException {
    File file = new File(storage, id + ".dat");
    try (BufferedReader in = new BufferedReader(new FileReader(file))) {
        return in.readLine();
    }
}
      
      



Como você pode ver, não há nenhum código aqui que decida o que fazer em caso de erro. E não está claro o que fazer - encerrar o programa, retornar "", nulo ou outra coisa? Portanto, as exceções são declaradas throws



e serão tratadas em algum lugar do chamador:





int initCounter(String name) throws IOException, NumberFormatException {
    try {
        return Integer.parseInt(readStoredData(name));
    } catch (FileNotFoundException e) {
        return 0;
    }
}
      
      



As exceções em Java são divididas em marcadas e não marcadas. Neste caso, IOException



ele é verificado - você deve declará-lo throws



e então processá-lo em algum lugar, o compilador irá verificar. NumberFormatException



não verificável - seu processamento permanece na consciência do programador e o compilador não controlará você.





Há também um terceiro tipo de exceção - erros fatais ( Error



), mas geralmente não faz sentido lidar com eles, então você não deve se preocupar com eles.





, – , .





:





  • ;





  • ( – ) .









- , . .





Scala?

Scala: ( ), .





Try[T]



– , , . Scala:





def readStoredData(id: String): Try[String] =
  Try {
    val file = new File(storage, s"$id.dat")
    val source = Source.fromFile(file)
    try source.getLines().next()
    finally source.close()
  }

def initCounter(name: String): Try[Int] = {
  readStoredData(name)
    .map(_.toInt)
    .recover {
      case _: FileNotFoundException => 0
    }
}
      
      



, , readStoredData



String



, Try[String]



– . Try Java – , .





:





  • ( Either[Error, T]



    , );





  • happy-path , (Try/get



    for/map/flatMap



    );





  • Java - , ( Java , ).





( Try[String]



– ). Option[T]



– , Future[T]



– ..





, – . / Java, ( ).





:





  1. FileNotFoundException



    ,





  2. IOException







:





def readStoredData(id: String): Option[Try[String]] = {
  val file = new File(storage, s"$id.dat")
  if (file.exists()) Some(
    Try {
      val source = Source.fromFile(file)
      try source.getLines().next()
      finally source.close()
    }
  )
  else None
}
      
      



Option[Try[String]]



, , :





  1. None







  2. Some(Success(string))







  3. Some(Failure(exception))



    – ,





Try



. Java , null. .





A abundância de tipos cria mais ruído visual e geralmente requer um código mais complexo ao trabalhar com vários efeitos ao mesmo tempo. Mas, em troca, ele fornece código autodocumentado e permite que o compilador encontre muitos erros.








All Articles