Una vez más sobre probar y probar

Excepciones marcadas y no

En definitiva, se necesitan excepciones para separar el escenario positivo (cuando todo va bien) del negativo (cuando ocurre un error y se interrumpe el escenario positivo). Esto es útil porque muy a menudo hay poca información en el código para manejar el error y necesita transmitir información sobre lo que sucedió anteriormente.





Por ejemplo, hay una función para leer un número de un archivo (o no un número, no 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 puede ver, aquí no hay ningún código que decida qué hacer en caso de error. Y no está claro qué hacer: ¿terminar el programa, devolver "", nulo o algo más? Por lo tanto, las excepciones se declaran throws



y se manejarán en algún lugar de la persona que llama:





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



Las excepciones en Java se dividen en marcadas y no marcadas. En este caso, IOException



está marcado: debe declararlo throws



y luego procesarlo en algún lugar, el compilador lo verificará. NumberFormatException



no verificable: su procesamiento permanece en la conciencia del programador y el compilador no lo controlará.





También hay un tercer tipo de excepción: errores fatales ( Error



), pero generalmente no tiene sentido manejarlos, por lo que no debe preocuparse por ellos.





, – , .





:





  • ;





  • ( – ) .









- , . .





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. .





La abundancia de tipos crea más ruido visual y, a menudo, requiere un código más complejo cuando se trabaja con varios efectos al mismo tiempo. Pero, a cambio, proporciona código autodocumentado y permite al compilador encontrar muchos errores.








All Articles