English 中文(简体)
如何避免当地变数,可能还没有初步形成?
原标题:How to avoid the local variable may not have been initialized ?
  • 时间:2009-10-18 17:34:29
  •  标签:
/*This is a program that calculates Internet advertising rates based on what features/options you choose.
 * 
 *  
 */

import java.util.Scanner;

public class InternetAdvertising 
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);

        int numberOfWords;      

        //I assigned 0 values to both as Eclipse suggested
        float textCost = 0;
        float linkCost = 0;     

        float graphicCost;

        //<=25 words is a flat fee of $.40 per word plus Base fee of $3.00 
        final float TEXT_FLAT_FEE = 0.40F;
        final float TEXT_BASE_FEE = 3.00F;

        //<=35 words is $.40 for the first 25 words and 
        //an additional $.35 per word up to and including 35 words plus Base fee of $3.00 
        final float LESS_OR_EQUAL_THAN_THIRTYFIVE = 0.35F;

        //Over 35 words is a flat fee of $.32 per word with no base fee
        final float MORE_THAN_THIRTYFIVE = 0.32F;


        System.out.println("Welcome!");

        System.out.print("Enter the number of words in your ad: ");
        numberOfWords = in.nextInt();

        if (numberOfWords <= 25)
        {
            textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * numberOfWords);
        }

        else if (numberOfWords <= 35)
        {
            textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * 25) + (numberOfWords - 25) * LESS_OR_EQUAL_THAN_THIRTYFIVE;
        }

        else if (numberOfWords > 35)
        {
            textCost = numberOfWords * MORE_THAN_THIRTYFIVE;
        }


        String addLink, advancePay;
        char link, advPay;

        final float LINK_FLAT_FEE = 14.95F;
        final float THREE_MONTH_ADV_DISCOUNT = 0.10F;

        System.out.print("Would you like to add a link (y = yes or n = no)? ");
        addLink = in.next();

        link = addLink.charAt(0);
        link = Character.toLowerCase(link); 

        if (link ==  y )
        {
            System.out.print("Would you like to pay 3 months in advance " + "(y = yes or n = no)? ");
            advancePay = in.next();

            advPay = advancePay.charAt(0);
            advPay = Character.toLowerCase(advPay);

            switch (advPay)
            {
                case  y :

                    linkCost = (3 * LINK_FLAT_FEE) - (3 * LINK_FLAT_FEE) * THREE_MONTH_ADV_DISCOUNT;

                    break;

                case  n :

                    linkCost = LINK_FLAT_FEE;

                    break;
            }               
        }

        else
        {
            linkCost = 0;
        }


        String addGraphic;
        char graphic;

        System.out.print("Would you like to add graphics/pictures” + “(S = Small, M = Medium, L = Large or N = None)? ");
        addGraphic = in.next();

        graphic = addGraphic.charAt(0);
        graphic = Character.toUpperCase(graphic);
        graphic = Character.toLowerCase(graphic);       
        switch (graphic)
        {
            case  s :

                graphicCost = 19.07F;

                break;

            case  m :

                graphicCost = 24.76F;

                break;

            case  l :

                graphicCost = 29.33F;

                break;

            default:
                graphicCost = 0;
        }


        float gst, totalBeforeGst, totalAfterGst;

        final float GST_RATE = 0.05F;

        totalBeforeGst = textCost + linkCost + graphicCost; //textCost & linkCost would not initialize

        gst = totalBeforeGst * GST_RATE;

        totalAfterGst = totalBeforeGst + (totalBeforeGst * GST_RATE);


        System.out.printf("		%-16s %11s
", "Category", "Cost");
        System.out.printf("		%-16s %11.2f
", "Text", textCost);  //linkCost would not initialize
        System.out.printf("		%-16s %11.2f
", "Link", linkCost);  //textCost would not initialize 
        System.out.printf("		%-16s %11.2f
", "Graphic", graphicCost);
        System.out.printf("		%-16s %11.2f
", "Total", totalBeforeGst);
        System.out.printf("		%-16s %11.2f
", "GST", gst);
        System.out.printf("		%-16s %11.2f
", "Total with GST", totalAfterGst);
    }   
}

我几乎照此法行事,Eclipse建议我把0个数值分配给textCostlinkCost。 是否有其他办法解决这一问题。 如果我没有给出0个数值的话,就会产生错误(当地变量三十可能尚未开始)。 难道有人可以向我解释,尽管我有两种变数,但为什么会发生这种情况?

感谢。

EDIT:我按照建议行事,只有在Im需要时,才宣布这些变量。 我还补充了一些意见。

最佳回答

摆在我面前的三条建议深入到法典中:

  • Declare variables as late as you can to make it easier to understand the code.
  • Refactor this giant method - it s unreadably huge at the moment.
  • Make the constants static final fields. They re not related to any particular call to the method, so they shouldn t be local variables.

现在就实际问题而言,最简单的办法是确保每一可能的流量都实际为does分配价值或作出例外规定。 因此,在<条码>文本编码<>上,将你的代码改为:

if (numberOfWords <= 25)
{
    textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * numberOfWords);
}
else if (numberOfWords <= 35)
{
    textCost = TEXT_BASE_FEE + (TEXT_FLAT_FEE * 25) + (numberOfWords - 25) * 
               LESS_OR_EQUAL_THAN_THIRTYFIVE;
}
else // Note - no condition.
{
    textCost = numberOfWords * MORE_THAN_THIRTYFIVE;
}

<代码>Cost,将您的开关声明改为:

switch (advPay)
{
    case  y :
        linkCost = (3 * LINK_FLAT_FEE) - 
                   (3 * LINK_FLAT_FEE) * THREE_MONTH_ADV_DISCOUNT;
        break;
    case  n :
        linkCost = LINK_FLAT_FEE;
        break;
    default:
        throw new Exception("Invalid value specified: " + advPay);
}

现在你们可能不想在这里提出一个例外。 你们可能要再次 lo,或像这样。 也许don t希望仅使用Exception,但您应想到您希望使用的确切例外类型do

不能做到这一点。 汇编者确定明确转让的规则是相对而言的直截了当。 如果你真的能够改变守则,使汇编者对此感到欣慰,那么你就只能给人一种怀疑的初始价值。 我建议尽可能避免这种情况。 在您的第一起案件中,实际价值总是被分配——但在第二起案件中,确实是weren t<>。 在<代码>advPay时,既无也无价值可导致后来出现难以直觉的问题。 编辑错误有助于你发现这种问题。

不过,我强烈建议你支持这一方法。 我怀疑你会发现,如果每一种方法中仅有大约10条编码线,而且每个变量在使用之前或首次使用时都宣布,那么,我们会非常容易理解为什么有些事情被无限期地分配。

EDIT:

奥凯,经彻底修改的法典如下。 我不想声称它是世界上最好的法典,而是:

  • It s more testable. You could easily write unit tests for each part of it. printAllCosts isn t terribly easily testable, but you could have an overload which took a Writer to print to - that would help.
  • Each bit of calculation is in a logical place. Links and graphics have a small set of possible values - Java enums are a natural fit here. (I m aware they may well be beyond your current skill level, but it s good to see what will be available.)
  • I m not using binary floating point numbers any more, because they re inappropriate for numbers. Instead, I m using an integer number of cents everywhere and converting to BigDecimal for display purposes. See my article on .NET floating point for more information - it s all relevant to Java really.
  • The advert itself is now encapsulated in a class. You could add a lot more information here as and when you needed to.
  • The code is in a package. Admittedly it s all in one file at the moment (which is why only the EntryPoint class is public) but that s just for the sake of Stack Overflow and me not having to open up Eclipse.
  • There s JavaDoc explaining what s going on - at least for a few methods. (I would probably add some more in real code. I m running out of time here.)
  • We validate user input, except for the word count - and we perform that validation in a single routine, which should be reasonably testable. We can then assume that whenever we ve asked for input, we ve got something valid.
  • The number of static methods in EntryPoint is slightly alarming. It doesn t feel terribly OO - but I find that s often the way around the entry point to a program. Note that there s nothing to do with fees in there - it s just the user interface, basically.

这里的代码比以前多,但比以前更可读和可以维持的代码。

package advertising;

import java.util.Scanner;
import java.math.BigDecimal;

/** The graphic style of an advert. */
enum Graphic
{
    NONE(0),
    SMALL(1907),
    MEDIUM(2476),
    LARGE(2933);

    private final int cost;

    private Graphic(int cost)
    {
        this.cost = cost;
    }

    /** Returns the cost in cents. */
    public int getCost()
    {
        return cost;
    }
}

/** The link payment plan for an advert. */
enum LinkPlan
{
    NONE(0),
    PREPAID(1495), // 1 month
    POSTPAID(1495 * 3 - (1495 * 3) / 10); // 10% discount for 3 months up-front

    private final int cost;

    private LinkPlan(int cost)
    {
        this.cost = cost;
    }

    /** Returns the cost in cents. */
    public int getCost()
    {
        return cost;
    }
}

class Advertisement
{
    private final int wordCount;
    private final LinkPlan linkPlan;
    private final Graphic graphic;

    public Advertisement(int wordCount, LinkPlan linkPlan, Graphic graphic)
    {
        this.wordCount = wordCount;
        this.linkPlan = linkPlan;
        this.graphic = graphic;
    }

    /**
     * Returns the fee for the words in the advert, in cents.
     * 
     * For up to 25 words, there s a flat fee of 40c per word and a base fee
     * of $3.00.
     * 
     * For 26-35 words inclusive, the fee for the first 25 words is as before,
     * but the per-word fee goes down to 35c for words 26-35.
     * 
     * For more than 35 words, there s a flat fee of 32c per word, and no
     * base fee.     
     */
    public int getWordCost()
    {
        if (wordCount > 35)
        {
            return 32 * wordCount;
        }
        // Apply flat fee always, then up to 25 words at 40 cents,
        // then the rest at 35 cents.
        return 300 + Math.min(wordCount, 25) * 40
                   + Math.min(wordCount - 25, 0) * 35;        
    }

    /**
     * Displays the costs associated with this advert.
     */
    public void printAllCosts()
    {
        System.out.printf("		%-16s %11s
", "Category", "Cost");
        printCost("Text", getWordCost());
        printCost("Link", linkPlan.getCost());
        printCost("Graphic", graphic.getCost());
        int total = getWordCost() + linkPlan.getCost() + graphic.getCost();
        printCost("Total", total);
        int gst = total / 20;
        printCost("GST", gst);
        printCost("Total with GST", total + gst);
    }

    private void printCost(String category, int cents)
    {
        BigDecimal dollars = new BigDecimal(cents).scaleByPowerOfTen(-2);
        System.out.printf("		%-16s %11.2f
", category, dollars);
    }
}

/**
 * The entry point for the program - takes user input, builds an 
 * Advertisement, and displays its cost.
 */
public class EntryPoint
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);

        System.out.println("Welcome!");
        int wordCount = readWordCount(scanner);
        LinkPlan linkPlan = readLinkPlan(scanner);
        Graphic graphic = readGraphic(scanner);

        Advertisement advert = new Advertisement(wordCount, linkPlan, graphic);
        advert.printAllCosts();
    }

    private static int readWordCount(Scanner scanner)
    {
        System.out.print("Enter the number of words in your ad: ");
        // Could add validation code in here
        return scanner.nextInt();
    }

    private static LinkPlan readLinkPlan(Scanner scanner)
    {
        System.out.print("Would you like to add a link (y = yes or n = no)? ");
        char addLink = readSingleCharacter(scanner, "yn");
        LinkPlan linkPlan;
        if (addLink ==  n )
        {
            return LinkPlan.NONE;
        }
        System.out.print("Would you like to pay 3 months in advance " +
                         "(y = yes or n = no)? ");
        char advancePay = readSingleCharacter(scanner, "yn");
        return advancePay ==  y  ? LinkPlan.PREPAID : LinkPlan.POSTPAID;
    }

    private static Graphic readGraphic(Scanner scanner)
    {
        System.out.print("Would you like to add graphics/pictures? " +
            "(s = small, m = medium, l = large or n = None)? ");
        char graphic = readSingleCharacter(scanner, "smln");
        switch (graphic)
        {
            case  s : return Graphic.SMALL;
            case  m : return Graphic.MEDIUM;
            case  l : return Graphic.LARGE;
            case  n : return Graphic.NONE;
            default:
                throw new IllegalStateException("Unexpected state; graphic=" +
                                                graphic);
        }
    }

    private static char readSingleCharacter(Scanner scanner,
                                            String validOptions)
    {
        while(true)
        {
            String input = scanner.next();
            if (input.length() != 1 || !validOptions.contains(input))
            {
                System.out.print("Invalid value. Please try again: ");
                continue;
            }
            return input.charAt(0);
        }
    }
}
问题回答

<代码>Cost在 == y advPay上不适用 y n

换言之,如果汇编者在使用当地变量之前不能先入选,就可以通过你的法典找到一条道路,那么你就会发现这一错误。

发生这种情况的原因是,转让是在条件未得到满足的情况下进行的,转让从未发生。

为了避免在条件之外分配价值(最常见的是0)的错误。

Eclipse进行分析后,就可以确定在每一条代码道路上分配变量是否具有足够的智慧,以认识到<代码>编号OfWords上的测试永远不会失败。 典型的情况是,由于不可能静态地评估每一种可能的状况,编辑/科学检查员赢得了评估其中任何一种情况的尝试。 如果你把最后的“如果”改为“船舶”,那么它就应当工作,因为无论正在测试何种条件,至少将进行<代码>textCost的转让。

顺便说一句是警告你,因为你们的初始化是在有条件内进行的。 如无满足条件,将不予采用。

if (numberOfWords <= 25)
    {
            //assign a value to textCost
    }
    else if (numberOfWords <= 35)
    {
            //assign a value to textCost
    }
    else if (numberOfWords > 35)
    {
            //assign a value to textCost
    }

Eclipse may ben t recognized that ( numberOfWords <= 35) and ( numberOfWords > 35) includes all potential.

您可以先将其宣布为0,也可以包括另外一项{},将其定为零。

对其他变量的类似解释。

错误信息告诉你,这些变量为t always初始。 这是因为您的初创只是在某些条件下才发生(如果说的话,就座)。 希望。

尽管你知道与数字相比的三个分支之一。 将对女监察员进行访问,编辑们不知道这一点。 错误地认为,有可能加入其他条款,案文CCost变量将保持不变。

Similarly with the switch (advPay). Even though you know that one of the two will be visited, the compiler doesn t.

Suggestion: Remove the else if (numberOfWords > 35) make it just an else.

关于<代码>switch (advPay),加入default。 在座各位可以安装<条码>,浏览新的AsertionError();。

避免此类问题的一个良好办法是在检查前将拟分配的变量定为finalun initialized。 这将迫使你在你能够使用/阅读之前确定价值。

final textCostTmp;
if (condition1) {
  textCostTmp = ...;
} else if (condition2) {
  textCostTmp = ...;
} 
// if you use textCostTmp here the compiler will complain that it is uninitialized !
textCost = textCostTmp;

To solve this DO NOT initialize the variable as you may miss the missing else case. The only proper solution is to add an else case to cover all possible cases ! I consider it bad practice to initialize non final variables except for some rare scenarios like a counter in a loop.

The proposed approach will force you to handle all possible cases, now and in the future (easier to maintain). The compiler is a bit stupid at times (cannot figure that numberOfWords > 35 is the else)...but the compiler is your ally not your enemy...





相关问题